From 9df0177f334ea02dc8c269b662edb3c013c7ffff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 7 Jan 2024 23:59:42 -0700 Subject: [PATCH] mingw: add the mingw stdio functions back MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We would rather use the ucrt for these, but sometimes dependencies on the mingw stdio functions creep in. 仕方ない. The cost is only paid if they are used; otherwise the symbols are garbage-collected at link time. --- lib/libc/mingw/stdio/mingw_asprintf.c | 32 + lib/libc/mingw/stdio/mingw_dummy__lock.c | 12 + lib/libc/mingw/stdio/mingw_fprintf.c | 58 + lib/libc/mingw/stdio/mingw_fprintfw.c | 9 + lib/libc/mingw/stdio/mingw_fscanf.c | 21 + lib/libc/mingw/stdio/mingw_fwscanf.c | 21 + lib/libc/mingw/stdio/mingw_lock.c | 102 + lib/libc/mingw/stdio/mingw_pformat.c | 3312 ++++++++++++++++++++++ lib/libc/mingw/stdio/mingw_pformat.h | 99 + lib/libc/mingw/stdio/mingw_pformatw.c | 9 + lib/libc/mingw/stdio/mingw_printf.c | 59 + lib/libc/mingw/stdio/mingw_printfw.c | 9 + lib/libc/mingw/stdio/mingw_scanf.c | 28 + lib/libc/mingw/stdio/mingw_snprintf.c | 40 + lib/libc/mingw/stdio/mingw_snprintfw.c | 9 + lib/libc/mingw/stdio/mingw_sprintf.c | 56 + lib/libc/mingw/stdio/mingw_sprintfw.c | 10 + lib/libc/mingw/stdio/mingw_sscanf.c | 20 + lib/libc/mingw/stdio/mingw_swscanf.c | 20 + lib/libc/mingw/stdio/mingw_vasprintf.c | 25 + lib/libc/mingw/stdio/mingw_vfprintf.c | 58 + lib/libc/mingw/stdio/mingw_vfprintfw.c | 9 + lib/libc/mingw/stdio/mingw_vfscanf.c | 1632 +++++++++++ lib/libc/mingw/stdio/mingw_vprintf.c | 58 + lib/libc/mingw/stdio/mingw_vprintfw.c | 9 + lib/libc/mingw/stdio/mingw_vsnprintf.c | 52 + lib/libc/mingw/stdio/mingw_vsnprintfw.c | 9 + lib/libc/mingw/stdio/mingw_vsprintf.c | 54 + lib/libc/mingw/stdio/mingw_vsprintfw.c | 10 + lib/libc/mingw/stdio/mingw_wscanf.c | 28 + lib/libc/mingw/stdio/mingw_wvfscanf.c | 1631 +++++++++++ 31 files changed, 7501 insertions(+) create mode 100644 lib/libc/mingw/stdio/mingw_asprintf.c create mode 100644 lib/libc/mingw/stdio/mingw_dummy__lock.c create mode 100644 lib/libc/mingw/stdio/mingw_fprintf.c create mode 100644 lib/libc/mingw/stdio/mingw_fprintfw.c create mode 100644 lib/libc/mingw/stdio/mingw_fscanf.c create mode 100644 lib/libc/mingw/stdio/mingw_fwscanf.c create mode 100644 lib/libc/mingw/stdio/mingw_lock.c create mode 100644 lib/libc/mingw/stdio/mingw_pformat.c create mode 100644 lib/libc/mingw/stdio/mingw_pformat.h create mode 100644 lib/libc/mingw/stdio/mingw_pformatw.c create mode 100644 lib/libc/mingw/stdio/mingw_printf.c create mode 100644 lib/libc/mingw/stdio/mingw_printfw.c create mode 100644 lib/libc/mingw/stdio/mingw_scanf.c create mode 100644 lib/libc/mingw/stdio/mingw_snprintf.c create mode 100644 lib/libc/mingw/stdio/mingw_snprintfw.c create mode 100644 lib/libc/mingw/stdio/mingw_sprintf.c create mode 100644 lib/libc/mingw/stdio/mingw_sprintfw.c create mode 100644 lib/libc/mingw/stdio/mingw_sscanf.c create mode 100644 lib/libc/mingw/stdio/mingw_swscanf.c create mode 100644 lib/libc/mingw/stdio/mingw_vasprintf.c create mode 100644 lib/libc/mingw/stdio/mingw_vfprintf.c create mode 100644 lib/libc/mingw/stdio/mingw_vfprintfw.c create mode 100644 lib/libc/mingw/stdio/mingw_vfscanf.c create mode 100644 lib/libc/mingw/stdio/mingw_vprintf.c create mode 100644 lib/libc/mingw/stdio/mingw_vprintfw.c create mode 100644 lib/libc/mingw/stdio/mingw_vsnprintf.c create mode 100644 lib/libc/mingw/stdio/mingw_vsnprintfw.c create mode 100644 lib/libc/mingw/stdio/mingw_vsprintf.c create mode 100644 lib/libc/mingw/stdio/mingw_vsprintfw.c create mode 100644 lib/libc/mingw/stdio/mingw_wscanf.c create mode 100644 lib/libc/mingw/stdio/mingw_wvfscanf.c diff --git a/lib/libc/mingw/stdio/mingw_asprintf.c b/lib/libc/mingw/stdio/mingw_asprintf.c new file mode 100644 index 0000000000..e55e235af0 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_asprintf.c @@ -0,0 +1,32 @@ +#define _GNU_SOURCE +#define __CRT__NO_INLINE + +#include +#include +#include + +int __mingw_asprintf(char ** __restrict__ ret, + const char * __restrict__ format, + ...) { + va_list ap; + int len; + va_start(ap,format); + /* Get Length */ + len = __mingw_vsnprintf(NULL,0,format,ap); + if (len < 0) goto _end; + /* +1 for \0 terminator. */ + *ret = malloc(len + 1); + /* Check malloc fail*/ + if (!*ret) { + len = -1; + goto _end; + } + /* Write String */ + __mingw_vsnprintf(*ret,len+1,format,ap); + /* Terminate explicitly */ + (*ret)[len] = '\0'; + _end: + va_end(ap); + return len; +} + diff --git a/lib/libc/mingw/stdio/mingw_dummy__lock.c b/lib/libc/mingw/stdio/mingw_dummy__lock.c new file mode 100644 index 0000000000..7afe665d0c --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_dummy__lock.c @@ -0,0 +1,12 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#include + +void __cdecl _lock(int locknum); +void __cdecl _unlock(int locknum); +void __cdecl _lock(__UNUSED_PARAM(int locknum)) { } +void __cdecl _unlock(__UNUSED_PARAM(int locknum)) { } diff --git a/lib/libc/mingw/stdio/mingw_fprintf.c b/lib/libc/mingw/stdio/mingw_fprintf.c new file mode 100644 index 0000000000..438941f7e3 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_fprintf.c @@ -0,0 +1,58 @@ +/* fprintf.c + * + * $Id: fprintf.c,v 1.1 2008/08/11 22:41:55 keithmarshall Exp $ + * + * Provides an implementation of the "fprintf" function, conforming + * generally to C99 and SUSv3/POSIX specifications, with extensions + * to support Microsoft's non-standard format specifications. This + * is included in libmingwex.a, whence it may replace the Microsoft + * function of the same name. + * + * Written by Keith Marshall + * + * This implementation of "fprintf" will normally be invoked by calling + * "__mingw_fprintf()" in preference to a direct reference to "fprintf()" + * itself; this leaves the MSVCRT implementation as the default, which + * will be deployed when user code invokes "fprint()". Users who then + * wish to use this implementation may either call "__mingw_fprintf()" + * directly, or may use conditional preprocessor defines, to redirect + * references to "fprintf()" to "__mingw_fprintf()". + * + * Compiling this module with "-D INSTALL_AS_DEFAULT" will change this + * recommended convention, such that references to "fprintf()" in user + * code will ALWAYS be redirected to "__mingw_fprintf()"; if this option + * is adopted, then users wishing to use the MSVCRT implementation of + * "fprintf()" will be forced to use a "back-door" mechanism to do so. + * Such a "back-door" mechanism is provided with MinGW, allowing the + * MSVCRT implementation to be called as "__msvcrt_fprintf()"; however, + * since users may not expect this behaviour, a standard libmingwex.a + * installation does not employ this option. + * + * + * 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. + * + */ +#include +#include + +#include "mingw_pformat.h" + +int __cdecl __fprintf (FILE *, const APICHAR *, ...) __MINGW_NOTHROW; + +int __cdecl __fprintf(FILE *stream, const APICHAR *fmt, ...) +{ + register int retval; + va_list argv; va_start( argv, fmt ); + _lock_file( stream ); + retval = __pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stream, 0, fmt, argv ); + _unlock_file( stream ); + va_end( argv ); + return retval; +} diff --git a/lib/libc/mingw/stdio/mingw_fprintfw.c b/lib/libc/mingw/stdio/mingw_fprintfw.c new file mode 100644 index 0000000000..e5ea41ee49 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_fprintfw.c @@ -0,0 +1,9 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 + +#include "mingw_fprintf.c" + diff --git a/lib/libc/mingw/stdio/mingw_fscanf.c b/lib/libc/mingw/stdio/mingw_fscanf.c new file mode 100644 index 0000000000..0c5192a5c8 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_fscanf.c @@ -0,0 +1,21 @@ +#include +#include +#include + +extern int __mingw_vfscanf (FILE *stream, const char *format, va_list argp); + +int __mingw_fscanf (FILE *stream, const char *format, ...); + +int +__mingw_fscanf (FILE *stream, const char *format, ...) +{ + va_list argp; + int r; + + va_start (argp, format); + r = __mingw_vfscanf (stream, format, argp); + va_end (argp); + + return r; +} + diff --git a/lib/libc/mingw/stdio/mingw_fwscanf.c b/lib/libc/mingw/stdio/mingw_fwscanf.c new file mode 100644 index 0000000000..018125ecf2 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_fwscanf.c @@ -0,0 +1,21 @@ +#include +#include +#include + +extern int __mingw_vfwscanf (FILE *stream, const wchar_t *format, va_list argp); + +int __mingw_fwscanf (FILE *stream, const wchar_t *format, ...); + +int +__mingw_fwscanf (FILE *stream, const wchar_t *format, ...) +{ + va_list argp; + int r; + + va_start (argp, format); + r = __mingw_vfwscanf (stream, format, argp); + va_end (argp); + + return r; +} + diff --git a/lib/libc/mingw/stdio/mingw_lock.c b/lib/libc/mingw/stdio/mingw_lock.c new file mode 100644 index 0000000000..ebc3c256fe --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_lock.c @@ -0,0 +1,102 @@ +#define _CRTIMP +#include +#include +#include "internal.h" + +/*** + * Copy of MS functions _lock_file, _unlock_file which are missing from + * msvcrt.dll and msvcr80.dll. They are needed to atomic/lock stdio + * functions (printf, fprintf, vprintf, vfprintf). We need exactly the same + * lock that MS uses in msvcrt.dll because we can mix mingw-w64 code with + * original MS functions (puts, fputs for example). +***/ + + +_CRTIMP void __cdecl _lock(int locknum); +_CRTIMP void __cdecl _unlock(int locknum); +#define _STREAM_LOCKS 16 +#define _IOLOCKED 0x8000 + + +/*** +* _lock_file - Lock a FILE +* +*Purpose: +* Assert the lock for a stdio-level file +* +*Entry: +* pf = __piob[] entry (pointer to a FILE or _FILEX) +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl _lock_file( FILE *pf ) +{ + /* + * The way the FILE (pointed to by pf) is locked depends on whether + * it is part of _iob[] or not + */ + if ( (pf >= __acrt_iob_func(0)) && (pf <= __acrt_iob_func(_IOB_ENTRIES-1)) ) + { + /* + * FILE lies in _iob[] so the lock lies in _locktable[]. + */ + _lock( _STREAM_LOCKS + (int)(pf - __acrt_iob_func(0)) ); + /* We set _IOLOCKED to indicate we locked the stream */ + pf->_flag |= _IOLOCKED; + } + else + /* + * Not part of _iob[]. Therefore, *pf is a _FILEX and the + * lock field of the struct is an initialized critical + * section. + */ + EnterCriticalSection( &(((_FILEX *)pf)->lock) ); +} + +void *__MINGW_IMP_SYMBOL(_lock_file) = _lock_file; + + +/*** +* _unlock_file - Unlock a FILE +* +*Purpose: +* Release the lock for a stdio-level file +* +*Entry: +* pf = __piob[] entry (pointer to a FILE or _FILEX) +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl _unlock_file( FILE *pf ) +{ + /* + * The way the FILE (pointed to by pf) is unlocked depends on whether + * it is part of _iob[] or not + */ + if ( (pf >= __acrt_iob_func(0)) && (pf <= __acrt_iob_func(_IOB_ENTRIES-1)) ) + { + /* + * FILE lies in _iob[] so the lock lies in _locktable[]. + * We reset _IOLOCKED to indicate we unlock the stream. + */ + pf->_flag &= ~_IOLOCKED; + _unlock( _STREAM_LOCKS + (int)(pf - __acrt_iob_func(0)) ); + } + else + /* + * Not part of _iob[]. Therefore, *pf is a _FILEX and the + * lock field of the struct is an initialized critical + * section. + */ + LeaveCriticalSection( &(((_FILEX *)pf)->lock) ); +} + +void *__MINGW_IMP_SYMBOL(_unlock_file) = _unlock_file; diff --git a/lib/libc/mingw/stdio/mingw_pformat.c b/lib/libc/mingw/stdio/mingw_pformat.c new file mode 100644 index 0000000000..87902aea27 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_pformat.c @@ -0,0 +1,3312 @@ +/* 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +/* 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 +/* 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 + +#ifndef _VALUES_H +/* + * values.h + * + */ +#define _VALUES_H + +#include + +#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 int __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 __pformat_fpreg_t init_fpreg_ldouble( long double val ) +{ + __pformat_fpreg_t x; + x.__pformat_fpreg_ldouble_t = val; + + if( sizeof( double ) == sizeof( long double ) ) + { + /* Here, __pformat_fpreg_t expects to be initialized with a 80 bit long + * double, but this platform doesn't have long doubles that differ from + * regular 64 bit doubles. Therefore manually convert the 64 bit float + * value to an 80 bit float value. + */ + int exp = (x.__pformat_fpreg_mantissa >> 52) & 0x7ff; + unsigned long long mant = x.__pformat_fpreg_mantissa & 0x000fffffffffffffULL; + int topbit = exp ? 1 : 0; + int signbit = x.__pformat_fpreg_mantissa >> 63; + + if (exp == 0x7ff) + exp = 0x7fff; + else if (exp != 0) + exp = exp - 1023 + 16383; + else if (mant != 0) { + /* Denormal when stored as a 64 bit double, but becomes a normal when + * converted to 80 bit long double form. */ + exp = 1 - 1023 + 16383; + while (!(mant & 0x0010000000000000ULL)) { + /* Normalize the mantissa. */ + mant <<= 1; + exp--; + } + topbit = 1; /* The top bit, which is implicit in the 64 bit form. */ + } + x.__pformat_fpreg_mantissa = (mant << 11) | ((unsigned long long)topbit << 63); + x.__pformat_fpreg_exponent = exp | (signbit << 15); + } + + return x; +} + +static +char *__pformat_cvt( int mode, long double val, 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 */ }; + __pformat_fpreg_t x = init_fpreg_ldouble( val ); + + k = __fpclassifyl( val ); + + /* 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. + */ + return __pformat_cvt( 2, x, 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. + */ + return __pformat_cvt( 3, x, 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; + + if (value.__pformat_fpreg_mantissa != 0 || + value.__pformat_fpreg_exponent != 0) + { + /* Reduce the exponent since the leading digit emited will start at + * the 4th bit from the highest order bit instead, the later being + * the leading digit of the floating point. Don't do this adjustment + * if the value is an actual zero. + */ + value.__pformat_fpreg_exponent -= 3; + } + + /* 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). + */ + + /* 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. We will + * discard a whole digit to match glibc's behavior. + */ + value.__pformat_fpreg_exponent += 4; + value.__pformat_fpreg_mantissa >>= 3; + } + + /* 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); + } + + /* Don't print anything if mantissa is zero unless we have to satisfy + * desired precision. + */ + if( value.__pformat_fpreg_mantissa || stream->precision > 0 ) + { + /* Encode the significant digits of the mantissa in hexadecimal + * ASCII notation, ready for transfer to the output stream... + */ + for( int i=stream->precision >= 15 || stream->precision < 0 ? 16 : stream->precision + 1; i>0; --i ) + { + /* taking the rightmost digit in each pass... + */ + unsigned c = value.__pformat_fpreg_mantissa & 0xF; + if( i == 1 ) + { + /* 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++ = '.'; + } + } + + 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 = init_fpreg_ldouble( 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. + */ + z.__pformat_fpreg_exponent = 1 - 0x3FFF; + } + } + 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 ); + } + } +} + +static +void __pformat_xdouble( double x, __pformat_t *stream ) +{ + /* Handler for `%la' and `%lA' format specifiers, (with argument + * value specified as `double' type). + */ + unsigned sign_bit = 0; + __pformat_fpreg_t z = init_fpreg_ldouble( (long double)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 the double value was a denormalized number, it might have been renormalized by + * the conversion to long double. We will redenormalize it. + */ + if( z.__pformat_fpreg_exponent != 0 && z.__pformat_fpreg_exponent <= (0x3FFF - 0x3FF) ) + { + int shifted = (0x3FFF - 0x3FF) - z.__pformat_fpreg_exponent + 1; + z.__pformat_fpreg_mantissa >>= shifted; + z.__pformat_fpreg_exponent += shifted; + } + + 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. + */ + z.__pformat_fpreg_exponent = 1 - 0x3FF + 3; + } + } + else + /* This argument represents a non-zero normal number; + * eliminate the bias from the exponent... + */ + z.__pformat_fpreg_exponent -= 0x3FFF - 3; + + /* Shift the mantissa so the leading 4 bits digit is 0 or 1. + * The exponent was also adjusted by 3 previously. + */ + z.__pformat_fpreg_mantissa >>= 3; + + /* 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; + + /* fallthrough */ + + 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; + + /* fallthrough */ + + 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... + */ + stream.flags &= ~PFORMAT_POSITIVE; +#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; + + /* fallthrough */ + + 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; + + /* fallthrough */ + + 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; + + /* fallthrough */ + + 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; + + /* fallthrough */ + + case 'A': + /* + * Hexadecimal floating point format; handles radix and + * exponent indicators in either upper or lower case... + */ + if( sizeof( double ) != sizeof( long double ) && stream.flags & PFORMAT_LDOUBLE ) + /* + * with a `long double' argument... + */ + __pformat_xldouble( va_arg( argv, long double ), &stream ); + + else + /* or just a `double'. + */ + __pformat_xdouble( 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; + } + + /* fallthrough */ + + 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; + #ifndef __BUILD_WIDEAPI + __pformat_putc( '%', &stream ); + #else + stream.width = stream.precision = PFORMAT_IGNORE; + __pformat_wputchars( L"%", 1, &stream ); + #endif + goto format_scan; + } + } + } + } + + else + /* We just parsed a character which is not included within any format + * specification; we simply emit it as a literal. + */ + #ifndef __BUILD_WIDEAPI + __pformat_putc( c, &stream ); + #else + if (literal_string_start == NULL) + literal_string_start = fmt - 1; + #endif + } + + /* When we have fully dispatched the format string, the return value is the + * total number of bytes we transferred to the output destination. + */ +#ifdef __BUILD_WIDEAPI + if (literal_string_start) + { + stream.width = stream.precision = PFORMAT_IGNORE; + __pformat_wputchars( literal_string_start, fmt - literal_string_start - 1, &stream ); + } +#endif + + return stream.count; +} + +/* $RCSfile: pformat.c,v $Revision: 1.9 $: end of file */ + diff --git a/lib/libc/mingw/stdio/mingw_pformat.h b/lib/libc/mingw/stdio/mingw_pformat.h new file mode 100644 index 0000000000..4777804322 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_pformat.h @@ -0,0 +1,99 @@ +#ifndef PFORMAT_H +/* + * pformat.h + * + * $Id: pformat.h,v 1.1 2008/07/28 23:24:20 keithmarshall Exp $ + * + * A private header, defining the `pformat' API; it is to be included + * in each compilation unit implementing any of the `printf' family of + * functions, but serves no useful purpose elsewhere. + * + * Written by Keith Marshall + * + * 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. + */ +#define PFORMAT_H + +/* The following macros reproduce definitions from _mingw.h, + * so that compilation will not choke, if using any compiler + * other than the MinGW implementation of GCC. + */ +#ifndef __cdecl +# ifdef __GNUC__ +# define __cdecl __attribute__((__cdecl__)) +# else +# define __cdecl +# endif +#endif + +#ifndef __MINGW_GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __MINGW_GNUC_PREREQ( major, minor )\ + (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) +# else +# define __MINGW_GNUC_PREREQ( major, minor ) +# endif +#endif + +#ifndef __MINGW_NOTHROW +# if __MINGW_GNUC_PREREQ( 3, 3 ) +# define __MINGW_NOTHROW __attribute__((__nothrow__)) +# else +# define __MINGW_NOTHROW +# endif +#endif + +#ifdef __BUILD_WIDEAPI +#define APICHAR wchar_t +#else +#define APICHAR char +#endif + +/* The following are the declarations specific to the `pformat' API... + */ +#define PFORMAT_TO_FILE 0x2000 +#define PFORMAT_NOLIMIT 0x4000 + +#if defined(__MINGW32__) || defined(__MINGW64__) + /* + * Map MinGW specific function names, for use in place of the generic + * implementation defined equivalent function names. + */ +#ifdef __BUILD_WIDEAPI +# define __pformat __mingw_wpformat +#define __fputc(X,STR) fputwc((wchar_t) (X), (STR)) + +# define __printf __mingw_wprintf +# define __fprintf __mingw_fwprintf +# define __sprintf __mingw_swprintf +# define __snprintf __mingw_snwprintf + +# define __vprintf __mingw_vwprintf +# define __vfprintf __mingw_vfwprintf +# define __vsprintf __mingw_vswprintf +# define __vsnprintf __mingw_vsnwprintf +#else +# define __pformat __mingw_pformat +#define __fputc(X,STR) fputc((X), (STR)) + +# define __printf __mingw_printf +# define __fprintf __mingw_fprintf +# define __sprintf __mingw_sprintf +# define __snprintf __mingw_snprintf + +# define __vprintf __mingw_vprintf +# define __vfprintf __mingw_vfprintf +# define __vsprintf __mingw_vsprintf +# define __vsnprintf __mingw_vsnprintf +#endif /* __BUILD_WIDEAPI */ +#endif + +int __cdecl __pformat(int, void *, int, const APICHAR *, va_list) __MINGW_NOTHROW; +#endif /* !defined PFORMAT_H */ diff --git a/lib/libc/mingw/stdio/mingw_pformatw.c b/lib/libc/mingw/stdio/mingw_pformatw.c new file mode 100644 index 0000000000..2c7cd1eade --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_pformatw.c @@ -0,0 +1,9 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 + +#include "mingw_pformat.c" + diff --git a/lib/libc/mingw/stdio/mingw_printf.c b/lib/libc/mingw/stdio/mingw_printf.c new file mode 100644 index 0000000000..fbd2e7d90b --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_printf.c @@ -0,0 +1,59 @@ +/* printf.c + * + * $Id: printf.c,v 1.1 2008/08/11 22:41:55 keithmarshall Exp $ + * + * Provides an implementation of the "printf" function, conforming + * generally to C99 and SUSv3/POSIX specifications, with extensions + * to support Microsoft's non-standard format specifications. This + * is included in libmingwex.a, whence it may replace the Microsoft + * function of the same name. + * + * Written by Keith Marshall + * + * This implementation of "printf" will normally be invoked by calling + * "__mingw_printf()" in preference to a direct reference to "printf()" + * itself; this leaves the MSVCRT implementation as the default, which + * will be deployed when user code invokes "print()". Users who then + * wish to use this implementation may either call "__mingw_printf()" + * directly, or may use conditional preprocessor defines, to redirect + * references to "printf()" to "__mingw_printf()". + * + * Compiling this module with "-D INSTALL_AS_DEFAULT" will change this + * recommended convention, such that references to "printf()" in user + * code will ALWAYS be redirected to "__mingw_printf()"; if this option + * is adopted, then users wishing to use the MSVCRT implementation of + * "printf()" will be forced to use a "back-door" mechanism to do so. + * Such a "back-door" mechanism is provided with MinGW, allowing the + * MSVCRT implementation to be called as "__msvcrt_printf()"; however, + * since users may not expect this behaviour, a standard libmingwex.a + * installation does not employ this option. + * + * + * 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. + * + */ +#include +#include + +#include "mingw_pformat.h" + +int __cdecl __printf(const APICHAR *, ...) __MINGW_NOTHROW; + +int __cdecl __printf(const APICHAR *fmt, ...) +{ + register int retval; + va_list argv; va_start( argv, fmt ); + _lock_file( stdout ); + retval = __pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stdout, 0, fmt, argv ); + _unlock_file( stdout ); + va_end( argv ); + return retval; +} + diff --git a/lib/libc/mingw/stdio/mingw_printfw.c b/lib/libc/mingw/stdio/mingw_printfw.c new file mode 100644 index 0000000000..db65c93e8c --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_printfw.c @@ -0,0 +1,9 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 + +#include "mingw_printf.c" + diff --git a/lib/libc/mingw/stdio/mingw_scanf.c b/lib/libc/mingw/stdio/mingw_scanf.c new file mode 100644 index 0000000000..ebec7d48bc --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_scanf.c @@ -0,0 +1,28 @@ +#include +#include +#include + +extern int __mingw_vfscanf (FILE *stream, const char *format, va_list argp); + +int __mingw_scanf (const char *format, ...); +int __mingw_vscanf (const char *format, va_list argp); + +int +__mingw_scanf (const char *format, ...) +{ + va_list argp; + int r; + + va_start (argp, format); + r = __mingw_vfscanf (stdin, format, argp); + va_end (argp); + + return r; +} + +int +__mingw_vscanf (const char *format, va_list argp) +{ + return __mingw_vfscanf (stdin, format, argp); +} + diff --git a/lib/libc/mingw/stdio/mingw_snprintf.c b/lib/libc/mingw/stdio/mingw_snprintf.c new file mode 100644 index 0000000000..0c0d76220b --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_snprintf.c @@ -0,0 +1,40 @@ +/* snprintf.c + * + * $Id: snprintf.c,v 1.3 2008/07/28 23:24:20 keithmarshall Exp $ + * + * Provides an implementation of the "snprintf" function, conforming + * generally to C99 and SUSv3/POSIX specifications, with extensions + * to support Microsoft's non-standard format specifications. This + * is included in libmingwex.a, replacing the redirection through + * libmoldnames.a, to the MSVCRT standard "_snprintf" function; (the + * standard MSVCRT function remains available, and may be invoked + * directly, using this fully qualified form of its name). + * + * Written by Keith Marshall + * + * 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. + * + */ + +#include +#include + +#include "mingw_pformat.h" + +int __cdecl __snprintf (APICHAR *, size_t, const APICHAR *fmt, ...) __MINGW_NOTHROW; +int __cdecl __vsnprintf (APICHAR *, size_t, const APICHAR *fmt, va_list) __MINGW_NOTHROW; + +int __cdecl __snprintf(APICHAR *buf, size_t length, const APICHAR *fmt, ...) +{ + va_list argv; va_start( argv, fmt ); + register int retval = __vsnprintf( buf, length, fmt, argv ); + va_end( argv ); + return retval; +} diff --git a/lib/libc/mingw/stdio/mingw_snprintfw.c b/lib/libc/mingw/stdio/mingw_snprintfw.c new file mode 100644 index 0000000000..b31cda2aec --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_snprintfw.c @@ -0,0 +1,9 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 + +#include "mingw_snprintf.c" + diff --git a/lib/libc/mingw/stdio/mingw_sprintf.c b/lib/libc/mingw/stdio/mingw_sprintf.c new file mode 100644 index 0000000000..4b46eb5322 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_sprintf.c @@ -0,0 +1,56 @@ +/* sprintf.c + * + * $Id: sprintf.c,v 1.1 2008/08/11 22:41:55 keithmarshall Exp $ + * + * Provides an implementation of the "sprintf" function, conforming + * generally to C99 and SUSv3/POSIX specifications, with extensions + * to support Microsoft's non-standard format specifications. This + * is included in libmingwex.a, whence it may replace the Microsoft + * function of the same name. + * + * Written by Keith Marshall + * + * This implementation of "sprintf" will normally be invoked by calling + * "__mingw_sprintf()" in preference to a direct reference to "sprintf()" + * itself; this leaves the MSVCRT implementation as the default, which + * will be deployed when user code invokes "sprint()". Users who then + * wish to use this implementation may either call "__mingw_sprintf()" + * directly, or may use conditional preprocessor defines, to redirect + * references to "sprintf()" to "__mingw_sprintf()". + * + * Compiling this module with "-D INSTALL_AS_DEFAULT" will change this + * recommended convention, such that references to "sprintf()" in user + * code will ALWAYS be redirected to "__mingw_sprintf()"; if this option + * is adopted, then users wishing to use the MSVCRT implementation of + * "sprintf()" will be forced to use a "back-door" mechanism to do so. + * Such a "back-door" mechanism is provided with MinGW, allowing the + * MSVCRT implementation to be called as "__msvcrt_sprintf()"; however, + * since users may not expect this behaviour, a standard libmingwex.a + * installation does not employ this option. + * + * + * 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. + * + */ +#include +#include + +#include "mingw_pformat.h" + +int __cdecl __sprintf (APICHAR *, const APICHAR *, ...) __MINGW_NOTHROW; + +int __cdecl __sprintf(APICHAR *buf, const APICHAR *fmt, ...) +{ + register int retval; + va_list argv; va_start( argv, fmt ); + buf[retval = __pformat( PFORMAT_NOLIMIT, buf, 0, fmt, argv )] = '\0'; + va_end( argv ); + return retval; +} diff --git a/lib/libc/mingw/stdio/mingw_sprintfw.c b/lib/libc/mingw/stdio/mingw_sprintfw.c new file mode 100644 index 0000000000..0baf1e4f38 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_sprintfw.c @@ -0,0 +1,10 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 +#define _CRT_NON_CONFORMING_SWPRINTFS 1 + +#include "mingw_sprintf.c" + diff --git a/lib/libc/mingw/stdio/mingw_sscanf.c b/lib/libc/mingw/stdio/mingw_sscanf.c new file mode 100644 index 0000000000..e1a8e8bfe3 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_sscanf.c @@ -0,0 +1,20 @@ +#include +#include + +extern int __mingw_vsscanf (const char *buf, const char *format, va_list argp); + +int __mingw_sscanf (const char *buf, const char *format, ...); + +int +__mingw_sscanf (const char *buf, const char *format, ...) +{ + va_list argp; + int r; + + va_start (argp, format); + r = __mingw_vsscanf (buf, format, argp); + va_end (argp); + + return r; +} + diff --git a/lib/libc/mingw/stdio/mingw_swscanf.c b/lib/libc/mingw/stdio/mingw_swscanf.c new file mode 100644 index 0000000000..47f9f34f15 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_swscanf.c @@ -0,0 +1,20 @@ +#include +#include + +extern int __mingw_vswscanf (const wchar_t *buf, const wchar_t *format, va_list argp); + +int __mingw_swscanf (const wchar_t *buf, const wchar_t *format, ...); + +int +__mingw_swscanf (const wchar_t *buf, const wchar_t *format, ...) +{ + va_list argp; + int r; + + va_start (argp, format); + r = __mingw_vswscanf (buf, format, argp); + va_end (argp); + + return r; +} + diff --git a/lib/libc/mingw/stdio/mingw_vasprintf.c b/lib/libc/mingw/stdio/mingw_vasprintf.c new file mode 100644 index 0000000000..8e3344a274 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vasprintf.c @@ -0,0 +1,25 @@ +#define _GNU_SOURCE +#define __CRT__NO_INLINE + +#include +#include +#include + +int __mingw_vasprintf(char ** __restrict__ ret, + const char * __restrict__ format, + va_list ap) { + int len; + /* Get Length */ + len = __mingw_vsnprintf(NULL,0,format,ap); + if (len < 0) return -1; + /* +1 for \0 terminator. */ + *ret = malloc(len + 1); + /* Check malloc fail*/ + if (!*ret) return -1; + /* Write String */ + __mingw_vsnprintf(*ret,len+1,format,ap); + /* Terminate explicitly */ + (*ret)[len] = '\0'; + return len; +} + diff --git a/lib/libc/mingw/stdio/mingw_vfprintf.c b/lib/libc/mingw/stdio/mingw_vfprintf.c new file mode 100644 index 0000000000..01ad01b579 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vfprintf.c @@ -0,0 +1,58 @@ +/* vfprintf.c + * + * $Id: vfprintf.c,v 1.1 2008/08/11 22:41:55 keithmarshall Exp $ + * + * Provides an implementation of the "vfprintf" function, conforming + * generally to C99 and SUSv3/POSIX specifications, with extensions + * to support Microsoft's non-standard format specifications. This + * is included in libmingwex.a, whence it may replace the Microsoft + * function of the same name. + * + * Written by Keith Marshall + * + * This implementation of "vfprintf" will normally be invoked by calling + * "__mingw_vfprintf()" in preference to a direct reference to "vfprintf()" + * itself; this leaves the MSVCRT implementation as the default, which + * will be deployed when user code invokes "vfprint()". Users who then + * wish to use this implementation may either call "__mingw_vfprintf()" + * directly, or may use conditional preprocessor defines, to redirect + * references to "vfprintf()" to "__mingw_vfprintf()". + * + * Compiling this module with "-D INSTALL_AS_DEFAULT" will change this + * recommended convention, such that references to "vfprintf()" in user + * code will ALWAYS be redirected to "__mingw_vfprintf()"; if this option + * is adopted, then users wishing to use the MSVCRT implementation of + * "vfprintf()" will be forced to use a "back-door" mechanism to do so. + * Such a "back-door" mechanism is provided with MinGW, allowing the + * MSVCRT implementation to be called as "__msvcrt_vfprintf()"; however, + * since users may not expect this behaviour, a standard libmingwex.a + * installation does not employ this option. + * + * + * 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. + * + */ +#include +#include + +#include "mingw_pformat.h" + +int __cdecl __vfprintf (FILE *, const APICHAR *, va_list) __MINGW_NOTHROW; + +int __cdecl __vfprintf(FILE *stream, const APICHAR *fmt, va_list argv) +{ + register int retval; + + _lock_file( stream ); + retval = __pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stream, 0, fmt, argv ); + _unlock_file( stream ); + + return retval; +} diff --git a/lib/libc/mingw/stdio/mingw_vfprintfw.c b/lib/libc/mingw/stdio/mingw_vfprintfw.c new file mode 100644 index 0000000000..0b86387657 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vfprintfw.c @@ -0,0 +1,9 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 + +#include "mingw_vfprintf.c" + diff --git a/lib/libc/mingw/stdio/mingw_vfscanf.c b/lib/libc/mingw/stdio/mingw_vfscanf.c new file mode 100644 index 0000000000..8ca81b54e1 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vfscanf.c @@ -0,0 +1,1632 @@ +/* + This Software is provided under the Zope Public License (ZPL) Version 2.1. + + Copyright (c) 2011 by the mingw-w64 project + + See the AUTHORS file for the list of contributors to the mingw-w64 project. + + This license has been certified as open source. It has also been designated + as GPL compatible by the Free Software Foundation (FSF). + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions in source code must retain the accompanying copyright + notice, this list of conditions, and the following disclaimer. + 2. Redistributions in binary form must reproduce the accompanying + copyright notice, this list of conditions, and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + 3. Names of the copyright holders must not be used to endorse or promote + products derived from this software without prior written permission + from the copyright holders. + 4. The right to distribute this software or to use it for any purpose does + not give you the right to use Servicemarks (sm) or Trademarks (tm) of + the copyright holders. Use of them is covered by separate agreement + with the copyright holders. + 5. If any files are modified, you must cause the modified files to carry + prominent notices stating that you changed the files and the date of + any change. + + Disclaimer + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define __LARGE_MBSTATE_T + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Helper flags for conversion. */ +#define IS_C 0x0001 +#define IS_S 0x0002 +#define IS_L 0x0004 +#define IS_LL 0x0008 +#define IS_SIGNED_NUM 0x0010 +#define IS_POINTER 0x0020 +#define IS_HEX_FLOAT 0x0040 +#define IS_SUPPRESSED 0x0080 +#define USE_GROUP 0x0100 +#define USE_GNU_ALLOC 0x0200 +#define USE_POSIX_ALLOC 0x0400 + +#define IS_ALLOC_USED (USE_GNU_ALLOC | USE_POSIX_ALLOC) + +/* internal stream structure with back-buffer. */ +typedef struct _IFP +{ + __extension__ union { + void *fp; + const char *str; + }; + int bch[1024]; + unsigned int is_string : 1; + int back_top; + unsigned int seen_eof : 1; +} _IFP; + +static void * +get_va_nth (va_list argp, unsigned int n) +{ + va_list ap; + if (!n) abort (); + va_copy (ap, argp); + while (--n > 0) + (void) va_arg(ap, void *); + return va_arg (ap, void *); +} + +static void +optimize_alloc (char **p, char *end, size_t alloc_sz) +{ + size_t need_sz; + char *h; + + if (!p || !*p) + return; + + need_sz = end - *p; + if (need_sz == alloc_sz) + return; + + if ((h = (char *) realloc (*p, need_sz)) != NULL) + *p = h; +} + +static void +back_ch (int c, _IFP *s, size_t *rin, int not_eof) +{ + if (!not_eof && c == EOF) + return; + if (s->is_string == 0) + { + FILE *fp = s->fp; + ungetc (c, fp); + rin[0] -= 1; + return; + } + rin[0] -= 1; + s->bch[s->back_top] = c; + s->back_top += 1; +} + +static int +in_ch (_IFP *s, size_t *rin) +{ + int r; + if (s->back_top) + { + s->back_top -= 1; + r = s->bch[s->back_top]; + rin[0] += 1; + } + else if (s->seen_eof) + { + return EOF; + } + else if (s->is_string) + { + const char *ps = s->str; + r = ((int) *ps) & 0xff; + ps++; + if (r != 0) + { + rin[0] += 1; + s->str = ps; + return r; + } + s->seen_eof = 1; + return EOF; + } + else + { + FILE *fp = (FILE *) s->fp; + r = getc (fp); + if (r != EOF) + rin[0] += 1; + else s->seen_eof = 1; + } + return r; +} + +static int +match_string (_IFP *s, size_t *rin, int *c, const char *str) +{ + int ch = *c; + + if (*str == 0) + return 1; + + if (*str != (char) tolower (ch)) + return 0; + ++str; + while (*str != 0) + { + if ((ch = in_ch (s, rin)) == EOF) + { + c[0] = ch; + return 0; + } + + if (*str != (char) tolower (ch)) + { + c[0] = ch; + return 0; + } + ++str; + } + c[0] = ch; + return 1; +} + +struct gcollect +{ + size_t count; + struct gcollect *next; + char **ptrs[32]; +}; + +static void +release_ptrs (struct gcollect **pt, char **wbuf) +{ + struct gcollect *pf; + size_t cnt; + + if (wbuf) + { + free (*wbuf); + *wbuf = NULL; + } + if (!pt || (pf = *pt) == NULL) + return; + while (pf != NULL) + { + struct gcollect *pf_sv = pf; + for (cnt = 0; cnt < pf->count; ++cnt) + { + free (*pf->ptrs[cnt]); + *pf->ptrs[cnt] = NULL; + } + pf = pf->next; + free (pf_sv); + } + *pt = NULL; +} + +static int +cleanup_return (int rval, struct gcollect **pfree, char **strp, char **wbuf) +{ + if (rval == EOF) + release_ptrs (pfree, wbuf); + else + { + if (pfree) + { + struct gcollect *pf = *pfree, *pf_sv; + while (pf != NULL) + { + pf_sv = pf; + pf = pf->next; + free (pf_sv); + } + *pfree = NULL; + } + if (strp != NULL) + { + free (*strp); + *strp = NULL; + } + if (wbuf) + { + free (*wbuf); + *wbuf = NULL; + } + } + return rval; +} + +static struct gcollect * +resize_gcollect (struct gcollect *pf) +{ + struct gcollect *np; + if (pf && pf->count < 32) + return pf; + np = malloc (sizeof (struct gcollect)); + np->count = 0; + np->next = pf; + return np; +} + +static char * +resize_wbuf (size_t wpsz, size_t *wbuf_max_sz, char *old) +{ + char *wbuf; + size_t nsz; + if (*wbuf_max_sz != wpsz) + return old; + nsz = (256 > (2 * wbuf_max_sz[0]) ? 256 : (2 * wbuf_max_sz[0])); + if (!old) + wbuf = (char *) malloc (nsz); + else + wbuf = (char *) realloc (old, nsz); + if (!wbuf) + { + if (old) + free (old); + } + else + *wbuf_max_sz = nsz; + return wbuf; +} + +static int +__mingw_sformat (_IFP *s, const char *format, va_list argp) +{ + const char *f = format; + struct gcollect *gcollect = NULL; + size_t read_in = 0, wbuf_max_sz = 0, cnt; + ssize_t str_sz = 0; + char *str = NULL, **pstr = NULL, *wbuf = NULL; + wchar_t *wstr = NULL; + int rval = 0, c = 0, ignore_ws = 0; + va_list arg; + unsigned char fc; + unsigned int npos; + int width, flags, base = 0, errno_sv; + size_t wbuf_cur_sz, read_in_sv, new_sz, n; + char seen_dot, seen_exp, is_neg, not_in; + char *tmp_wbuf_ptr, buf[MB_LEN_MAX]; + const char *lc_decimal_point, *lc_thousands_sep; + mbstate_t state, cstate; + union { + unsigned long long ull; + unsigned long ul; + long long ll; + long l; + } cv_val; + + arg = argp; + + if (!s || s->fp == NULL || !format) + { + errno = EINVAL; + return EOF; + } + + memset (&state, 0, sizeof (state)); + + lc_decimal_point = localeconv()->decimal_point; + lc_thousands_sep = localeconv()->thousands_sep; + if (lc_thousands_sep != NULL && *lc_thousands_sep == 0) + lc_thousands_sep = NULL; + + while (*f != 0) + { + if (!isascii ((unsigned char) *f)) + { + int len; + + if ((len = mbrlen (f, strlen (f), &state)) > 0) + { + do + { + if ((c = in_ch (s, &read_in)) == EOF || c != (unsigned char) *f++) + { + back_ch (c, s, &read_in, 1); + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + } + } + while (--len > 0); + + continue; + } + } + + fc = *f++; + if (fc != '%') + { + if (isspace (fc)) + ignore_ws = 1; + else + { + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + if (ignore_ws) + { + ignore_ws = 0; + if (isspace (c)) + { + do + { + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + } + while (isspace (c)); + } + } + + if (c != fc) + { + back_ch (c, s, &read_in, 0); + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + continue; + } + + width = flags = 0; + npos = 0; + wbuf_cur_sz = 0; + + if (isdigit ((unsigned char) *f)) + { + const char *svf = f; + npos = (unsigned char) *f++ - '0'; + while (isdigit ((unsigned char) *f)) + npos = npos * 10 + ((unsigned char) *f++ - '0'); + if (*f != '$') + { + npos = 0; + f = svf; + } + else + f++; + } + + do + { + if (*f == '*') + flags |= IS_SUPPRESSED; + else if (*f == '\'') + { + if (lc_thousands_sep) + flags |= USE_GROUP; + } + else if (*f == 'I') + { + /* we don't support locale's digits (i18N), but ignore it for now silently. */ + ; +#ifdef _WIN32 + if (f[1] == '6' && f[2] == '4') + { + flags |= IS_LL | IS_L; + f += 2; + } + else if (f[1] == '3' && f[2] == '2') + { + flags |= IS_L; + f += 2; + } + else + { +#ifdef _WIN64 + flags |= IS_LL | IS_L; +#else + flags |= IS_L; +#endif + } +#endif + } + else + break; + ++f; + } + while (1); + + while (isdigit ((unsigned char) *f)) + width = width * 10 + ((unsigned char) *f++ - '0'); + + if (!width) + width = -1; + + switch (*f) + { + case 'h': + ++f; + flags |= (*f == 'h' ? IS_C : IS_S); + if (*f == 'h') + ++f; + break; + case 'l': + ++f; + flags |= (*f == 'l' ? IS_LL : 0) | IS_L; + if (*f == 'l') + ++f; + break; + case 'q': case 'L': + ++f; + flags |= IS_LL | IS_L; + break; + case 'a': + if (f[1] != 's' && f[1] != 'S' && f[1] != '[') + break; + ++f; + flags |= USE_GNU_ALLOC; + break; + case 'm': + flags |= USE_POSIX_ALLOC; + ++f; + if (*f == 'l') + { + flags |= IS_L; + f++; + } + break; + case 'z': +#ifdef _WIN64 + flags |= IS_LL | IS_L; +#else + flags |= IS_L; +#endif + ++f; + break; + case 'j': + if (sizeof (uintmax_t) > sizeof (unsigned long)) + flags |= IS_LL; + else if (sizeof (uintmax_t) > sizeof (unsigned int)) + flags |= IS_L; + ++f; + break; + case 't': +#ifdef _WIN64 + flags |= IS_LL; +#else + flags |= IS_L; +#endif + ++f; + break; + case 0: + return cleanup_return (rval, &gcollect, pstr, &wbuf); + default: + break; + } + + if (*f == 0) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + fc = *f++; + if (ignore_ws || (fc != '[' && fc != 'c' && fc != 'C' && fc != 'n')) + { + errno_sv = errno; + errno = 0; + do + { + if ((c == EOF || (c = in_ch (s, &read_in)) == EOF) + && errno == EINTR) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + } + while (isspace (c)); + + ignore_ws = 0; + errno = errno_sv; + back_ch (c, s, &read_in, 0); + } + + switch (fc) + { + case 'c': + if ((flags & IS_L) != 0) + fc = 'C'; + break; + case 's': + if ((flags & IS_L) != 0) + fc = 'S'; + break; + } + + switch (fc) + { + case '%': + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + if (c != fc) + { + back_ch (c, s, &read_in, 1); + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + break; + + case 'n': + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_LL) != 0) + *(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = read_in; + else if ((flags & IS_L) != 0) + *(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = read_in; + else if ((flags & IS_S) != 0) + *(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = read_in; + else if ((flags & IS_C) != 0) + *(npos != 0 ? (char *) get_va_nth (argp, npos) : va_arg (arg, char *)) = read_in; + else + *(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = read_in; + } + break; + + case 'c': + if (width == -1) + width = 1; + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = (width > 1024 ? 1024 : width); + if ((str = *pstr = (char *) malloc (str_sz)) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + str = (char *) get_va_nth (argp, npos); + else + str = va_arg (arg, char *); + if (!str) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + do + { + if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz)) + { + new_sz = str_sz + (str_sz >= width ? width - 1 : str_sz); + while ((str = (char *) realloc (*pstr, new_sz)) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!str) + { + release_ptrs (&gcollect, &wbuf); + return EOF; + } + *pstr = str; + str += str_sz; + str_sz = new_sz; + } + *str++ = c; + } + while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); + } + else + while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + optimize_alloc (pstr, str, str_sz); + pstr = NULL; + ++rval; + } + + break; + + case 'C': + if (width == -1) + width = 1; + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + str_sz = (width > 1024 ? 1024 : width); + *pstr = (char *) malloc (str_sz * sizeof (wchar_t)); + if ((wstr = (wchar_t *) *pstr) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + wstr = (wchar_t *) get_va_nth (argp, npos); + else + wstr = va_arg (arg, wchar_t *); + if (!wstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + memset (&cstate, 0, sizeof (cstate)); + + do + { + buf[0] = c; + + if ((flags & IS_SUPPRESSED) == 0 && (flags & IS_ALLOC_USED) != 0 + && wstr == ((wchar_t *) *pstr + str_sz)) + { + new_sz = str_sz + (str_sz > width ? width - 1 : str_sz); + + while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!wstr) + { + release_ptrs (&gcollect, &wbuf); + return EOF; + } + *pstr = (char *) wstr; + wstr += str_sz; + str_sz = new_sz; + } + + while (1) + { + n = mbrtowc ((flags & IS_SUPPRESSED) == 0 ? wstr : NULL, buf, 1, &cstate); + + if (n == (size_t) -2) + { + if ((c = in_ch (s, &read_in)) == EOF) + { + errno = EILSEQ; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + buf[0] = c; + continue; + } + + if (n != 1) + { + errno = EILSEQ; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + break; + } + + ++wstr; + } + while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); + pstr = NULL; + ++rval; + } + break; + + case 's': + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = 100; + if ((str = *pstr = (char *) malloc (100)) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + str = (char *) get_va_nth (argp, npos); + else + str = va_arg (arg, char *); + if (!str) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + do + { + if (isspace (c)) + { + back_ch (c, s, &read_in, 1); + break; + } + + if ((flags & IS_SUPPRESSED) == 0) + { + *str++ = c; + if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz)) + { + new_sz = str_sz * 2; + + while ((str = (char *) realloc (*pstr, new_sz)) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!str) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + (*pstr)[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = str; + str += str_sz; + str_sz = new_sz; + } + } + } + while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != EOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + *str++ = 0; + optimize_alloc (pstr, str, str_sz); + pstr = NULL; + ++rval; + } + break; + + case 'S': + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = 100; + *pstr = (char *) malloc (100 * sizeof (wchar_t)); + if ((wstr = (wchar_t *) *pstr) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + wstr = (wchar_t *) get_va_nth (argp, npos); + else + wstr = va_arg (arg, wchar_t *); + if (!wstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + memset (&cstate, 0, sizeof (cstate)); + + do + { + if (isspace (c)) + { + back_ch (c, s, &read_in, 1); + break; + } + + buf[0] = c; + + while (1) + { + n = mbrtowc ((flags & IS_SUPPRESSED) == 0 ? wstr : NULL, buf, 1, &cstate); + + if (n == (size_t) -2) + { + if ((c = in_ch (s, &read_in)) == EOF) + { + errno = EILSEQ; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + buf[0] = c; + continue; + } + + if (n != 1) + { + errno = EILSEQ; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + ++wstr; + break; + } + + if ((flags & IS_SUPPRESSED) == 0 && (flags & IS_ALLOC_USED) != 0 + && wstr == ((wchar_t *) *pstr + str_sz)) + { + new_sz = str_sz * 2; + while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!wstr) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + ((wchar_t *) (*pstr))[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = (char *) wstr; + wstr += str_sz; + str_sz = new_sz; + } + } + while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != EOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + *wstr++ = 0; + optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); + pstr = NULL; + ++rval; + } + break; + + case 'd': case 'i': + case 'o': case 'p': + case 'u': + case 'x': case 'X': + switch (fc) + { + case 'd': + flags |= IS_SIGNED_NUM; + base = 10; + break; + case 'i': + flags |= IS_SIGNED_NUM; + base = 0; + break; + case 'o': + base = 8; + break; + case 'p': + base = 16; + flags &= ~(IS_S | IS_LL | IS_L); + #ifdef _WIN64 + flags |= IS_LL; + #endif + flags |= IS_L | IS_POINTER; + break; + case 'u': + base = 10; + break; + case 'x': case 'X': + base = 16; + break; + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + if (c == '+' || c == '-') + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + if (width > 0) + --width; + c = in_ch (s, &read_in); + } + if (width != 0 && c == '0') + { + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + c = in_ch (s, &read_in); + + if (width != 0 && tolower (c) == 'x') + { + if (!base) + base = 16; + if (base == 16) + { + if (width > 0) + --width; + c = in_ch (s, &read_in); + } + } + else if (!base) + base = 8; + } + + if (!base) + base = 10; + + while (c != EOF && width != 0) + { + if (base == 16) + { + if (!isxdigit (c)) + break; + } + else if (!isdigit (c) || (int) (c - '0') >= base) + { + const char *p = lc_thousands_sep; + int remain; + + if (base != 10 || (flags & USE_GROUP) == 0) + break; + remain = width > 0 ? width : INT_MAX; + while ((unsigned char) *p == c && remain >= 0) + { + /* As our conversion routines aren't supporting thousands + separators, we are filtering them here. */ + + ++p; + if (*p == 0 || !remain || (c = in_ch (s, &read_in)) == EOF) + break; + --remain; + } + + if (*p != 0) + { + if (p > lc_thousands_sep) + { + back_ch (c, s, &read_in, 0); + while (--p > lc_thousands_sep) + back_ch ((unsigned char) *p, s, &read_in, 1); + c = (unsigned char) *p; + } + break; + } + + if (width > 0) + width = remain; + --wbuf_cur_sz; + } + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + if (width > 0) + --width; + + c = in_ch (s, &read_in); + } + + if (!wbuf_cur_sz || (wbuf_cur_sz == 1 && (wbuf[0] == '+' || wbuf[0] == '-'))) + { + if (!wbuf_cur_sz && (flags & IS_POINTER) != 0 + && match_string (s, &read_in, &c, "(nil)")) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = '0'; + } + else + { + back_ch (c, s, &read_in, 0); + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + else + back_ch (c, s, &read_in, 0); + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = 0; + + if ((flags & IS_LL)) + { + if (flags & IS_SIGNED_NUM) + cv_val.ll = strtoll (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); + else + cv_val.ull = strtoull (wbuf, &tmp_wbuf_ptr, base); + } + else + { + if (flags & IS_SIGNED_NUM) + cv_val.l = strtol (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); + else + cv_val.ul = strtoul (wbuf, &tmp_wbuf_ptr, base); + } + if (wbuf == tmp_wbuf_ptr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_SIGNED_NUM) != 0) + { + if ((flags & IS_LL) != 0) + *(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = cv_val.ll; + else if ((flags & IS_L) != 0) + *(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = cv_val.l; + else if ((flags & IS_S) != 0) + *(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = (short) cv_val.l; + else if ((flags & IS_C) != 0) + *(npos != 0 ? (signed char *) get_va_nth (argp, npos) : va_arg (arg, signed char *)) = (signed char) cv_val.ul; + else + *(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = (int) cv_val.l; + } + else + { + if ((flags & IS_LL) != 0) + *(npos != 0 ? (unsigned long long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long long *)) = cv_val.ull; + else if ((flags & IS_L) != 0) + *(npos != 0 ? (unsigned long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long *)) = cv_val.ul; + else if ((flags & IS_S) != 0) + *(npos != 0 ? (unsigned short *) get_va_nth (argp, npos) : va_arg (arg, unsigned short *)) + = (unsigned short) cv_val.ul; + else if ((flags & IS_C) != 0) + *(npos != 0 ? (unsigned char *) get_va_nth (argp, npos) : va_arg (arg, unsigned char *)) = (unsigned char) cv_val.ul; + else + *(npos != 0 ? (unsigned int *) get_va_nth (argp, npos) : va_arg (arg, unsigned int *)) = (unsigned int) cv_val.ul; + } + ++rval; + } + break; + + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + case 'a': case 'A': + if (width > 0) + --width; + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + seen_dot = seen_exp = 0; + is_neg = (c == '-' ? 1 : 0); + + if (c == '-' || c == '+') + { + if (width == 0 || (c = in_ch (s, &read_in)) == EOF) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + } + + if (tolower (c) == 'n') + { + const char *match_txt = "nan"; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + ++match_txt; + do + { + if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0]) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + } + while (*match_txt != 0); + } + else if (tolower (c) == 'i') + { + const char *match_txt = "inf"; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + ++match_txt; + do + { + if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0]) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + } + while (*match_txt != 0); + + if (width != 0 && (c = in_ch (s, &read_in)) != EOF && tolower (c) == 'i') + { + match_txt = "inity"; + + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + + do + { + if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0]) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + } + while (*match_txt != 0); + } + else if (width != 0 && c != EOF) + back_ch (c, s, &read_in, 0); + } + else + { + not_in = 'e'; + if (width != 0 && c == '0') + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + c = in_ch (s, &read_in); + if (width > 0) + --width; + if (width != 0 && tolower (c) == 'x') + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + flags |= IS_HEX_FLOAT; + not_in = 'p'; + + flags &= ~USE_GROUP; + c = in_ch (s, &read_in); + if (width > 0) + --width; + } + } + + while (1) + { + if (isdigit (c) || (!seen_exp && (flags & IS_HEX_FLOAT) != 0 && isxdigit (c)) + || (seen_exp && wbuf[wbuf_cur_sz - 1] == not_in && (c == '-' || c == '+'))) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + } + else if (wbuf_cur_sz > 0 && !seen_exp && (char) tolower (c) == not_in) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = not_in; + seen_exp = seen_dot = 1; + } + else + { + const char *p = lc_decimal_point; + int remain = width > 0 ? width : INT_MAX; + + if (! seen_dot) + { + while ((unsigned char) *p == c && remain >= 0) + { + ++p; + if (*p == 0 || !remain || (c = in_ch (s, &read_in)) == EOF) + break; + --remain; + } + } + + if (*p == 0) + { + for (p = lc_decimal_point; *p != 0; ++p) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = (unsigned char) *p; + } + if (width > 0) + width = remain; + seen_dot = 1; + } + else + { + const char *pp = lc_thousands_sep; + + if (!seen_dot && (flags & USE_GROUP) != 0) + { + while ((pp - lc_thousands_sep) < (p - lc_decimal_point) + && *pp == lc_decimal_point[(pp - lc_thousands_sep)]) + ++pp; + if ((pp - lc_thousands_sep) == (p - lc_decimal_point)) + { + while ((unsigned char) *pp == c && remain >= 0) + { + ++pp; + if (*pp == 0 || !remain || (c = in_ch (s, &read_in)) == EOF) + break; + --remain; + } + } + } + + if (pp != NULL && *pp == 0) + { + /* As our conversion routines aren't supporting thousands + separators, we are filtering them here. */ + if (width > 0) + width = remain; + } + else + { + back_ch (c, s, &read_in, 0); + break; + } + } + } + + if (width == 0 || (c = in_ch (s, &read_in)) == EOF) + break; + + if (width > 0) + --width; + } + + if (!wbuf_cur_sz || ((flags & IS_HEX_FLOAT) != 0 && wbuf_cur_sz == 2)) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = 0; + + if ((flags & IS_LL) != 0) + { + long double ld; + ld = __mingw_strtold (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); + if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) + *(npos != 0 ? (long double *) get_va_nth (argp, npos) : va_arg (arg, long double *)) = is_neg ? -ld : ld; + } + else if ((flags & IS_L) != 0) + { + double d; + d = (double) __mingw_strtold (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); + if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) + *(npos != 0 ? (double *) get_va_nth (argp, npos) : va_arg (arg, double *)) = is_neg ? -d : d; + } + else + { + float d = __mingw_strtof (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); + if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) + *(npos != 0 ? (float *) get_va_nth (argp, npos) : va_arg (arg, float *)) = is_neg ? -d : d; + } + + if (wbuf == tmp_wbuf_ptr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + ++rval; + break; + + case '[': + if ((flags & IS_L) != 0) + { + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = 100; + *pstr = (char *) malloc (100 * sizeof (wchar_t)); + + if ((wstr = (wchar_t *) *pstr) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + wstr = (wchar_t *) get_va_nth (argp, npos); + else + wstr = va_arg (arg, wchar_t *); + if (!wstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + } + else if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = 100; + if ((str = *pstr = (char *) malloc (100)) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + str = (char *) get_va_nth (argp, npos); + else + str = va_arg (arg, char *); + if (!str) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + not_in = (*f == '^' ? 1 : 0); + if (*f == '^') + f++; + + if (width < 0) + width = INT_MAX; + + if (wbuf_max_sz < 256) + { + wbuf_max_sz = 256; + if (wbuf) + free (wbuf); + wbuf = (char *) malloc (wbuf_max_sz); + } + memset (wbuf, 0, 256); + + fc = *f; + if (fc == ']' || fc == '-') + { + wbuf[fc] = 1; + ++f; + } + + while ((fc = *f++) != 0 && fc != ']') + { + if (fc == '-' && *f != 0 && *f != ']' && (unsigned char) f[-2] <= (unsigned char) *f) + { + for (fc = (unsigned char) f[-2]; fc < (unsigned char) *f; ++fc) + wbuf[fc] = 1; + } + else + wbuf[fc] = 1; + } + + if (!fc) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_L) != 0) + { + read_in_sv = read_in; + cnt = 0; + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + memset (&cstate, 0, sizeof (cstate)); + + do + { + if (wbuf[c] == not_in) + { + back_ch (c, s, &read_in, 1); + break; + } + + if ((flags & IS_SUPPRESSED) == 0) + { + buf[0] = c; + n = mbrtowc (wstr, buf, 1, &cstate); + + if (n == (size_t) -2) + { + ++cnt; + continue; + } + cnt = 0; + + ++wstr; + if ((flags & IS_ALLOC_USED) != 0 && wstr == ((wchar_t *) *pstr + str_sz)) + { + new_sz = str_sz * 2; + while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!wstr) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + ((wchar_t *) (*pstr))[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + else + rval = EOF; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + *pstr = (char *) wstr; + wstr += str_sz; + str_sz = new_sz; + } + } + + if (--width <= 0) + break; + } + while ((c = in_ch (s, &read_in)) != EOF); + + if (cnt != 0) + { + errno = EILSEQ; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + if (read_in_sv == read_in) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + + if ((flags & IS_SUPPRESSED) == 0) + { + *wstr++ = 0; + optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); + pstr = NULL; + ++rval; + } + } + else + { + read_in_sv = read_in; + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + do + { + if (wbuf[c] == not_in) + { + back_ch (c, s, &read_in, 1); + break; + } + + if ((flags & IS_SUPPRESSED) == 0) + { + *str++ = c; + if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz)) + { + new_sz = str_sz * 2; + + while ((str = (char *) realloc (*pstr, new_sz)) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!str) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + (*pstr)[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + else + rval = EOF; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + *pstr = str; + str += str_sz; + str_sz = new_sz; + } + } + } + while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); + + if (read_in_sv == read_in) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + *str++ = 0; + optimize_alloc (pstr, str, str_sz); + pstr = NULL; + ++rval; + } + } + break; + + default: + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if (ignore_ws) + { + while (isspace ((c = in_ch (s, &read_in)))); + back_ch (c, s, &read_in, 0); + } + + return cleanup_return (rval, &gcollect, pstr, &wbuf); +} + +int +__mingw_vfscanf (FILE *s, const char *format, va_list argp) +{ + _IFP ifp; + memset (&ifp, 0, sizeof (_IFP)); + ifp.fp = s; + return __mingw_sformat (&ifp, format, argp); +} + +int +__mingw_vsscanf (const char *s, const char *format, va_list argp) +{ + _IFP ifp; + memset (&ifp, 0, sizeof (_IFP)); + ifp.str = s; + ifp.is_string = 1; + return __mingw_sformat (&ifp, format, argp); +} + diff --git a/lib/libc/mingw/stdio/mingw_vprintf.c b/lib/libc/mingw/stdio/mingw_vprintf.c new file mode 100644 index 0000000000..4098d49d1b --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vprintf.c @@ -0,0 +1,58 @@ +/* vprintf.c + * + * $Id: vprintf.c,v 1.1 2008/08/11 22:41:55 keithmarshall Exp $ + * + * Provides an implementation of the "vprintf" function, conforming + * generally to C99 and SUSv3/POSIX specifications, with extensions + * to support Microsoft's non-standard format specifications. This + * is included in libmingwex.a, whence it may replace the Microsoft + * function of the same name. + * + * Written by Keith Marshall + * + * This implementation of "vprintf" will normally be invoked by calling + * "__mingw_vprintf()" in preference to a direct reference to "vprintf()" + * itself; this leaves the MSVCRT implementation as the default, which + * will be deployed when user code invokes "vprint()". Users who then + * wish to use this implementation may either call "__mingw_vprintf()" + * directly, or may use conditional preprocessor defines, to redirect + * references to "vprintf()" to "__mingw_vprintf()". + * + * Compiling this module with "-D INSTALL_AS_DEFAULT" will change this + * recommended convention, such that references to "vprintf()" in user + * code will ALWAYS be redirected to "__mingw_vprintf()"; if this option + * is adopted, then users wishing to use the MSVCRT implementation of + * "vprintf()" will be forced to use a "back-door" mechanism to do so. + * Such a "back-door" mechanism is provided with MinGW, allowing the + * MSVCRT implementation to be called as "__msvcrt_vprintf()"; however, + * since users may not expect this behaviour, a standard libmingwex.a + * installation does not employ this option. + * + * + * 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. + * + */ +#include +#include + +#include "mingw_pformat.h" + +int __cdecl __vprintf (const APICHAR *, va_list) __MINGW_NOTHROW; + +int __cdecl __vprintf(const APICHAR *fmt, va_list argv) +{ + register int retval; + + _lock_file( stdout ); + retval = __pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stdout, 0, fmt, argv ); + _unlock_file( stdout ); + + return retval; +} diff --git a/lib/libc/mingw/stdio/mingw_vprintfw.c b/lib/libc/mingw/stdio/mingw_vprintfw.c new file mode 100644 index 0000000000..2c81073e6b --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vprintfw.c @@ -0,0 +1,9 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 + +#include "mingw_vprintf.c" + diff --git a/lib/libc/mingw/stdio/mingw_vsnprintf.c b/lib/libc/mingw/stdio/mingw_vsnprintf.c new file mode 100644 index 0000000000..b477300f97 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vsnprintf.c @@ -0,0 +1,52 @@ +/* vsnprintf.c + * + * $Id: vsnprintf.c,v 1.3 2008/07/28 23:24:20 keithmarshall Exp $ + * + * Provides an implementation of the "vsnprintf" function, conforming + * generally to C99 and SUSv3/POSIX specifications, with extensions + * to support Microsoft's non-standard format specifications. This + * is included in libmingwex.a, replacing the redirection through + * libmoldnames.a, to the MSVCRT standard "_vsnprintf" function; (the + * standard MSVCRT function remains available, and may be invoked + * directly, using this fully qualified form of its name). + * + * Written by Keith Marshall + * + * 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. + * + */ + +#include +#include + +#include "mingw_pformat.h" + +int __cdecl __vsnprintf (APICHAR *, size_t, const APICHAR *fmt, va_list) __MINGW_NOTHROW; +int __cdecl __vsnprintf(APICHAR *buf, size_t length, const APICHAR *fmt, va_list argv ) +{ + register int retval; + + if( length == (size_t)(0) ) + /* + * No buffer; simply compute and return the size required, + * without actually emitting any data. + */ + return __pformat( 0, buf, 0, fmt, argv); + + /* If we get to here, then we have a buffer... + * Emit data up to the limit of buffer length less one, + * then add the requisite NUL terminator. + */ + retval = __pformat( 0, buf, --length, fmt, argv ); + buf[retval < (int) length ? retval : (int)length] = '\0'; + + return retval; +} + diff --git a/lib/libc/mingw/stdio/mingw_vsnprintfw.c b/lib/libc/mingw/stdio/mingw_vsnprintfw.c new file mode 100644 index 0000000000..fb3dfa5e47 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vsnprintfw.c @@ -0,0 +1,9 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 + +#include "mingw_vsnprintf.c" + diff --git a/lib/libc/mingw/stdio/mingw_vsprintf.c b/lib/libc/mingw/stdio/mingw_vsprintf.c new file mode 100644 index 0000000000..af9793d384 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vsprintf.c @@ -0,0 +1,54 @@ +/* vsprintf.c + * + * $Id: vsprintf.c,v 1.1 2008/08/11 22:41:55 keithmarshall Exp $ + * + * Provides an implementation of the "vsprintf" function, conforming + * generally to C99 and SUSv3/POSIX specifications, with extensions + * to support Microsoft's non-standard format specifications. This + * is included in libmingwex.a, whence it may replace the Microsoft + * function of the same name. + * + * Written by Keith Marshall + * + * This implementation of "vsprintf" will normally be invoked by calling + * "__mingw_vsprintf()" in preference to a direct reference to "vsprintf()" + * itself; this leaves the MSVCRT implementation as the default, which + * will be deployed when user code invokes "vsprint()". Users who then + * wish to use this implementation may either call "__mingw_vsprintf()" + * directly, or may use conditional preprocessor defines, to redirect + * references to "vsprintf()" to "__mingw_vsprintf()". + * + * Compiling this module with "-D INSTALL_AS_DEFAULT" will change this + * recommended convention, such that references to "vsprintf()" in user + * code will ALWAYS be redirected to "__mingw_vsprintf()"; if this option + * is adopted, then users wishing to use the MSVCRT implementation of + * "vsprintf()" will be forced to use a "back-door" mechanism to do so. + * Such a "back-door" mechanism is provided with MinGW, allowing the + * MSVCRT implementation to be called as "__msvcrt_vsprintf()"; however, + * since users may not expect this behaviour, a standard libmingwex.a + * installation does not employ this option. + * + * + * 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. + * + */ +#include +#include + +#include "mingw_pformat.h" + +int __cdecl __vsprintf (APICHAR *, const APICHAR *, va_list) __MINGW_NOTHROW; + +int __cdecl __vsprintf(APICHAR *buf, const APICHAR *fmt, va_list argv) +{ + register int retval; + buf[retval = __pformat( PFORMAT_NOLIMIT, buf, 0, fmt, argv )] = '\0'; + return retval; +} diff --git a/lib/libc/mingw/stdio/mingw_vsprintfw.c b/lib/libc/mingw/stdio/mingw_vsprintfw.c new file mode 100644 index 0000000000..fd8808bf47 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_vsprintfw.c @@ -0,0 +1,10 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 +#define _CRT_NON_CONFORMING_SWPRINTFS 1 + +#include "mingw_vsprintf.c" + diff --git a/lib/libc/mingw/stdio/mingw_wscanf.c b/lib/libc/mingw/stdio/mingw_wscanf.c new file mode 100644 index 0000000000..6202a79b50 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_wscanf.c @@ -0,0 +1,28 @@ +#include +#include +#include + +extern int __mingw_vfwscanf (FILE *stream, const wchar_t *format, va_list argp); + +int __mingw_wscanf (const wchar_t *format, ...); +int __mingw_vwscanf (const wchar_t *format, va_list argp); + +int +__mingw_wscanf (const wchar_t *format, ...) +{ + va_list argp; + int r; + + va_start (argp, format); + r = __mingw_vfwscanf (stdin, format, argp); + va_end (argp); + + return r; +} + +int +__mingw_vwscanf (const wchar_t *format, va_list argp) +{ + return __mingw_vfwscanf (stdin, format, argp); +} + diff --git a/lib/libc/mingw/stdio/mingw_wvfscanf.c b/lib/libc/mingw/stdio/mingw_wvfscanf.c new file mode 100644 index 0000000000..81cb46b661 --- /dev/null +++ b/lib/libc/mingw/stdio/mingw_wvfscanf.c @@ -0,0 +1,1631 @@ +/* + This Software is provided under the Zope Public License (ZPL) Version 2.1. + + Copyright (c) 2011 by the mingw-w64 project + + See the AUTHORS file for the list of contributors to the mingw-w64 project. + + This license has been certified as open source. It has also been designated + as GPL compatible by the Free Software Foundation (FSF). + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions in source code must retain the accompanying copyright + notice, this list of conditions, and the following disclaimer. + 2. Redistributions in binary form must reproduce the accompanying + copyright notice, this list of conditions, and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + 3. Names of the copyright holders must not be used to endorse or promote + products derived from this software without prior written permission + from the copyright holders. + 4. The right to distribute this software or to use it for any purpose does + not give you the right to use Servicemarks (sm) or Trademarks (tm) of + the copyright holders. Use of them is covered by separate agreement + with the copyright holders. + 5. If any files are modified, you must cause the modified files to carry + prominent notices stating that you changed the files and the date of + any change. + + Disclaimer + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define __LARGE_MBSTATE_T + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CP_UTF8 +#define CP_UTF8 65001 +#endif + +#ifndef MB_ERR_INVALID_CHARS +#define MB_ERR_INVALID_CHARS 0x00000008 +#endif + +/* Helper flags for conversion. */ +#define IS_C 0x0001 +#define IS_S 0x0002 +#define IS_L 0x0004 +#define IS_LL 0x0008 +#define IS_SIGNED_NUM 0x0010 +#define IS_POINTER 0x0020 +#define IS_HEX_FLOAT 0x0040 +#define IS_SUPPRESSED 0x0080 +#define USE_GROUP 0x0100 +#define USE_GNU_ALLOC 0x0200 +#define USE_POSIX_ALLOC 0x0400 + +#define IS_ALLOC_USED (USE_GNU_ALLOC | USE_POSIX_ALLOC) + +/* internal stream structure with back-buffer. */ +typedef struct _IFP +{ + __extension__ union { + void *fp; + const wchar_t *str; + }; + int bch[1024]; + unsigned int is_string : 1; + int back_top; + unsigned int seen_eof : 1; +} _IFP; + +static void * +get_va_nth (va_list argp, unsigned int n) +{ + va_list ap; + if (!n) + abort (); + va_copy (ap, argp); + while (--n > 0) + (void) va_arg(ap, void *); + return va_arg (ap, void *); +} + +static void +optimize_alloc (char **p, char *end, size_t alloc_sz) +{ + size_t need_sz; + char *h; + + if (!p || !*p) + return; + + need_sz = end - *p; + if (need_sz == alloc_sz) + return; + + if ((h = (char *) realloc (*p, need_sz)) != NULL) + *p = h; +} + +static void +back_ch (int c, _IFP *s, size_t *rin, int not_eof) +{ + if (!not_eof && c == WEOF) + return; + if (s->is_string == 0) + { + FILE *fp = s->fp; + ungetwc (c, fp); + rin[0] -= 1; + return; + } + rin[0] -= 1; + s->bch[s->back_top] = c; + s->back_top += 1; +} + +static int +in_ch (_IFP *s, size_t *rin) +{ + int r; + if (s->back_top) + { + s->back_top -= 1; + r = s->bch[s->back_top]; + rin[0] += 1; + } + else if (s->seen_eof) + { + return WEOF; + } + else if (s->is_string) + { + const wchar_t *ps = s->str; + r = ((int) *ps) & 0xffff; + ps++; + if (r != 0) + { + rin[0] += 1; + s->str = ps; + return r; + } + s->seen_eof = 1; + return WEOF; + } + else + { + FILE *fp = (FILE *) s->fp; + r = getwc (fp); + if (r != WEOF) + rin[0] += 1; + else s->seen_eof = 1; + } + return r; +} + +static int +match_string (_IFP *s, size_t *rin, wint_t *c, const wchar_t *str) +{ + int ch = *c; + + if (*str == 0) + return 1; + + if (*str != (wchar_t) towlower (ch)) + return 0; + ++str; + while (*str != 0) + { + if ((ch = in_ch (s, rin)) == WEOF) + { + c[0] = ch; + return 0; + } + + if (*str != (wchar_t) towlower (ch)) + { + c[0] = ch; + return 0; + } + ++str; + } + c[0] = ch; + return 1; +} + +struct gcollect +{ + size_t count; + struct gcollect *next; + char **ptrs[32]; +}; + +static void +release_ptrs (struct gcollect **pt, wchar_t **wbuf) +{ + struct gcollect *pf; + size_t cnt; + + if (wbuf) + { + free (*wbuf); + *wbuf = NULL; + } + if (!pt || (pf = *pt) == NULL) + return; + while (pf != NULL) + { + struct gcollect *pf_sv = pf; + for (cnt = 0; cnt < pf->count; ++cnt) + { + free (*pf->ptrs[cnt]); + *pf->ptrs[cnt] = NULL; + } + pf = pf->next; + free (pf_sv); + } + *pt = NULL; +} + +static int +cleanup_return (int rval, struct gcollect **pfree, char **strp, wchar_t **wbuf) +{ + if (rval == EOF) + release_ptrs (pfree, wbuf); + else + { + if (pfree) + { + struct gcollect *pf = *pfree, *pf_sv; + while (pf != NULL) + { + pf_sv = pf; + pf = pf->next; + free (pf_sv); + } + *pfree = NULL; + } + if (strp != NULL) + { + free (*strp); + *strp = NULL; + } + if (wbuf) + { + free (*wbuf); + *wbuf = NULL; + } + } + return rval; +} + +static struct gcollect * +resize_gcollect (struct gcollect *pf) +{ + struct gcollect *np; + if (pf && pf->count < 32) + return pf; + np = malloc (sizeof (struct gcollect)); + np->count = 0; + np->next = pf; + return np; +} + +static wchar_t * +resize_wbuf (size_t wpsz, size_t *wbuf_max_sz, wchar_t *old) +{ + wchar_t *wbuf; + size_t nsz; + if (*wbuf_max_sz != wpsz) + return old; + nsz = (256 > (2 * wbuf_max_sz[0]) ? 256 : (2 * wbuf_max_sz[0])); + if (!old) + wbuf = (wchar_t *) malloc (nsz * sizeof (wchar_t)); + else + wbuf = (wchar_t *) realloc (old, nsz * sizeof (wchar_t)); + if (!wbuf) + { + if (old) + free (old); + } + else + *wbuf_max_sz = nsz; + return wbuf; +} + +static int +__mingw_swformat (_IFP *s, const wchar_t *format, va_list argp) +{ + const wchar_t *f = format; + struct gcollect *gcollect = NULL; + size_t read_in = 0, wbuf_max_sz = 0; + ssize_t str_sz = 0; + char *str = NULL, **pstr = NULL;; + wchar_t *wstr = NULL, *wbuf = NULL; + wint_t c = 0, rval = 0; + int ignore_ws = 0; + va_list arg; + size_t wbuf_cur_sz, str_len, read_in_sv, new_sz, n; + unsigned int fc, npos; + int width, flags, base = 0, errno_sv, clen; + char seen_dot, seen_exp, is_neg, *nstr, buf[MB_LEN_MAX]; + wchar_t wc, not_in, *tmp_wbuf_ptr, *temp_wbuf_end, *wbuf_iter; + wint_t lc_decimal_point, lc_thousands_sep; + mbstate_t state; + union { + unsigned long long ull; + unsigned long ul; + long long ll; + long l; + } cv_val; + + arg = argp; + + if (!s || s->fp == NULL || !format) + { + errno = EINVAL; + return EOF; + } + + memset (&state, 0, sizeof(state)); + clen = mbrtowc( &wc, localeconv()->decimal_point, 16, &state); + lc_decimal_point = (clen > 0 ? wc : '.'); + memset( &state, 0, sizeof( state ) ); + clen = mbrtowc( &wc, localeconv()->thousands_sep, 16, &state); + lc_thousands_sep = (clen > 0 ? wc : 0); + + while (*f != 0) + { + fc = *f++; + if (fc != '%') + { + if (iswspace (fc)) + ignore_ws = 1; + else + { + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + if (ignore_ws) + { + ignore_ws = 0; + if (iswspace (c)) + { + do + { + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + } + while (iswspace (c)); + } + } + + if (c != fc) + { + back_ch (c, s, &read_in, 0); + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + continue; + } + + width = flags = 0; + npos = 0; + wbuf_cur_sz = 0; + + if (iswdigit ((unsigned int) *f)) + { + const wchar_t *svf = f; + npos = (unsigned int) *f++ - '0'; + while (iswdigit ((unsigned int) *f)) + npos = npos * 10 + ((unsigned int) *f++ - '0'); + if (*f != '$') + { + npos = 0; + f = svf; + } + else + f++; + } + + do + { + if (*f == '*') + flags |= IS_SUPPRESSED; + else if (*f == '\'') + { + if (lc_thousands_sep) + flags |= USE_GROUP; + } + else if (*f == 'I') + { + /* we don't support locale's digits (i18N), but ignore it for now silently. */ + ; +#ifdef _WIN32 + if (f[1] == '6' && f[2] == '4') + { + flags |= IS_LL | IS_L; + f += 2; + } + else if (f[1] == '3' && f[2] == '2') + { + flags |= IS_L; + f += 2; + } + else + { +#ifdef _WIN64 + flags |= IS_LL | IS_L; +#else + flags |= IS_L; +#endif + } +#endif + } + else + break; + ++f; + } + while (1); + + while (iswdigit ((unsigned char) *f)) + width = width * 10 + ((unsigned char) *f++ - '0'); + + if (!width) + width = -1; + + switch (*f) + { + case 'h': + ++f; + flags |= (*f == 'h' ? IS_C : IS_S); + if (*f == 'h') + ++f; + break; + case 'l': + ++f; + flags |= (*f == 'l' ? IS_LL : 0) | IS_L; + if (*f == 'l') + ++f; + break; + case 'q': case 'L': + ++f; + flags |= IS_LL | IS_L; + break; + case 'a': + if (f[1] != 's' && f[1] != 'S' && f[1] != '[') + break; + ++f; + flags |= USE_GNU_ALLOC; + break; + case 'm': + flags |= USE_POSIX_ALLOC; + ++f; + if (*f == 'l') + { + flags |= IS_L; + ++f; + } + break; + case 'z': +#ifdef _WIN64 + flags |= IS_LL | IS_L; +#else + flags |= IS_L; +#endif + ++f; + break; + case 'j': + if (sizeof (uintmax_t) > sizeof (unsigned long)) + flags |= IS_LL; + else if (sizeof (uintmax_t) > sizeof (unsigned int)) + flags |= IS_L; + ++f; + break; + case 't': +#ifdef _WIN64 + flags |= IS_LL; +#else + flags |= IS_L; +#endif + ++f; + break; + case 0: + return cleanup_return (rval, &gcollect, pstr, &wbuf); + default: + break; + } + + if (*f == 0) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + fc = *f++; + if (ignore_ws || (fc != '[' && fc != 'c' && fc != 'C' && fc != 'n')) + { + errno_sv = errno; + errno = 0; + do + { + if ((c == WEOF || (c = in_ch (s, &read_in)) == WEOF) && errno == EINTR) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + } + while (iswspace (c)); + + ignore_ws = 0; + errno = errno_sv; + back_ch (c, s, &read_in, 0); + } + + switch (fc) + { + case 'c': + if ((flags & IS_L) != 0) + fc = 'C'; + break; + case 's': + if ((flags & IS_L) != 0) + fc = 'S'; + break; + } + + switch (fc) + { + case '%': + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + if (c != fc) + { + back_ch (c, s, &read_in, 1); + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + break; + + case 'n': + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_LL) != 0) + *(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = read_in; + else if ((flags & IS_L) != 0) + *(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = read_in; + else if ((flags & IS_S) != 0) + *(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = read_in; + else if ((flags & IS_C) != 0) + *(npos != 0 ? (char *) get_va_nth (argp, npos) : va_arg (arg, char *)) = read_in; + else + *(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = read_in; + } + break; + + case 'c': + if (width == -1) + width = 1; + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + str_sz = 100; + if ((str = *pstr = (char *) malloc (100)) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + str = (char *) get_va_nth (argp, npos); + else + str = va_arg (arg, char *); + if (!str) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + memset (&state, 0, sizeof (state)); + + do + { + if ((flags & IS_SUPPRESSED) == 0 && (flags & USE_POSIX_ALLOC) != 0 + && (str + MB_CUR_MAX) >= (*pstr + str_sz)) + { + new_sz = str_sz * 2; + str_len = (str - *pstr); + while ((nstr = (char *) realloc (*pstr, new_sz)) == NULL + && new_sz > (str_len + MB_CUR_MAX)) + new_sz = str_len + MB_CUR_MAX; + if (!nstr) + { + release_ptrs (&gcollect, &wbuf); + return EOF; + } + *pstr = nstr; + str = nstr + str_len; + str_sz = new_sz; + } + + n = wcrtomb ((flags & IS_SUPPRESSED) == 0 ? str : NULL, c, &state); + if (n == (size_t) -1LL) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + str += n; + } + while (--width > 0 && (c = in_ch (s, &read_in)) != WEOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + optimize_alloc (pstr, str, str_sz); + pstr = NULL; + ++rval; + } + + break; + + case 'C': + if (width == -1) + width = 1; + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + str_sz = (width > 1024 ? 1024 : width); + *pstr = (char *) malloc (str_sz * sizeof (wchar_t)); + if ((wstr = (wchar_t *) *pstr) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + + if ((wstr = (wchar_t *) *pstr) != NULL) + { + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + } + else + { + if (npos != 0) + wstr = (wchar_t *) get_va_nth (argp, npos); + else + wstr = va_arg (arg, wchar_t *); + if (!wstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + do + { + if ((flags & IS_ALLOC_USED) != 0 + && wstr == ((wchar_t *) *pstr + str_sz)) + { + new_sz = str_sz + (str_sz > width ? width - 1 : str_sz); + while ((wstr = (wchar_t *) realloc (*pstr, + new_sz * sizeof (wchar_t))) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!wstr) + { + release_ptrs (&gcollect, &wbuf); + return EOF; + } + *pstr = (char *) wstr; + wstr += str_sz; + str_sz = new_sz; + } + *wstr++ = c; + } + while (--width > 0 && (c = in_ch (s, &read_in)) != WEOF); + } + else + { + while (--width > 0 && (c = in_ch (s, &read_in)) != WEOF); + } + + if ((flags & IS_SUPPRESSED) == 0) + { + optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); + pstr = NULL; + ++rval; + } + break; + + case 's': + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + str_sz = 100; + if ((str = *pstr = (char *) malloc (100)) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + str = (char *) get_va_nth (argp, npos); + else + str = va_arg (arg, char *); + if (!str) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + memset (&state, 0, sizeof (state)); + + do + { + if (iswspace (c)) + { + back_ch (c, s, &read_in, 1); + break; + } + + { + if ((flags & IS_SUPPRESSED) == 0 && (flags & IS_ALLOC_USED) != 0 + && (str + MB_CUR_MAX) >= (*pstr + str_sz)) + { + new_sz = str_sz * 2; + str_len = (str - *pstr); + + while ((nstr = (char *) realloc (*pstr, new_sz)) == NULL + && new_sz > (str_len + MB_CUR_MAX)) + new_sz = str_len + MB_CUR_MAX; + if (!nstr) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + (*pstr)[str_len] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = nstr; + str = nstr + str_len; + str_sz = new_sz; + } + + n = wcrtomb ((flags & IS_SUPPRESSED) == 0 ? str : NULL, c, + &state); + if (n == (size_t) -1LL) + { + errno = EILSEQ; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + str += n; + } + } + while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != WEOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + n = wcrtomb (buf, 0, &state); + if (n > 0 && (flags & IS_ALLOC_USED) != 0 + && (str + n) >= (*pstr + str_sz)) + { + str_len = (str - *pstr); + + if ((nstr = (char *) realloc (*pstr, str_len + n + 1)) == NULL) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + (*pstr)[str_len] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = nstr; + str = nstr + str_len; + str_sz = str_len + n + 1; + } + + if (n) + { + memcpy (str, buf, n); + str += n; + } + *str++ = 0; + + optimize_alloc (pstr, str, str_sz); + pstr = NULL; + ++rval; + } + break; + + case 'S': + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + str_sz = 100; + *pstr = (char *) malloc (100 * sizeof (wchar_t)); + if ((wstr = (wchar_t *) *pstr) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + wstr = (wchar_t *) get_va_nth (argp, npos); + else + wstr = va_arg (arg, wchar_t *); + if (!wstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + do + { + if (iswspace (c)) + { + back_ch (c, s, &read_in, 1); + break; + } + + if ((flags & IS_SUPPRESSED) == 0) + { + *wstr++ = c; + if ((flags & IS_ALLOC_USED) != 0 && wstr == ((wchar_t *) *pstr + str_sz)) + { + new_sz = str_sz * 2; + + while ((wstr = (wchar_t *) realloc (*pstr, + new_sz * sizeof (wchar_t))) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!wstr) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + ((wchar_t *) (*pstr))[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = (char *) wstr; + wstr += str_sz; + str_sz = new_sz; + } + } + } + while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != WEOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + *wstr++ = 0; + + optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); + pstr = NULL; + ++rval; + } + break; + + case 'd': case 'i': + case 'o': case 'p': + case 'u': + case 'x': case 'X': + switch (fc) + { + case 'd': + flags |= IS_SIGNED_NUM; + base = 10; + break; + case 'i': + flags |= IS_SIGNED_NUM; + base = 0; + break; + case 'o': + base = 8; + break; + case 'p': + base = 16; + flags &= ~(IS_S | IS_LL | IS_L); + #ifdef _WIN64 + flags |= IS_LL; + #endif + flags |= IS_L | IS_POINTER; + break; + case 'u': + base = 10; + break; + case 'x': case 'X': + base = 16; + break; + } + + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + if (c == '+' || c == '-') + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + if (width > 0) + --width; + c = in_ch (s, &read_in); + } + + if (width != 0 && c == '0') + { + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + c = in_ch (s, &read_in); + + if (width != 0 && towlower (c) == 'x') + { + if (!base) + base = 16; + if (base == 16) + { + if (width > 0) + --width; + c = in_ch (s, &read_in); + } + } + else if (!base) + base = 8; + } + + if (!base) + base = 10; + + while (c != WEOF && width != 0) + { + if (base == 16) + { + if (!iswxdigit (c)) + break; + } + else if (!iswdigit (c) || (int) (c - '0') >= base) + { + if (base != 10 || (flags & USE_GROUP) == 0 || c != lc_thousands_sep) + break; + } + if (c != lc_thousands_sep) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + } + + if (width > 0) + --width; + + c = in_ch (s, &read_in); + } + + if (!wbuf_cur_sz || (wbuf_cur_sz == 1 && (wbuf[0] == '+' || wbuf[0] == '-'))) + { + if (!wbuf_cur_sz && (flags & IS_POINTER) != 0 + && match_string (s, &read_in, &c, L"(nil)")) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = '0'; + } + else + { + back_ch (c, s, &read_in, 0); + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + else + back_ch (c, s, &read_in, 0); + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = 0; + + if ((flags & IS_LL) != 0) + { + if ((flags & IS_SIGNED_NUM) != 0) + cv_val.ll = wcstoll (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); + else + cv_val.ull = wcstoull (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); + } + else + { + if ((flags & IS_SIGNED_NUM) != 0) + cv_val.l = wcstol (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); + else + cv_val.ul = wcstoul (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); + } + if (wbuf == tmp_wbuf_ptr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_SIGNED_NUM) != 0) + { + if ((flags & IS_LL) != 0) + *(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = cv_val.ll; + else if ((flags & IS_L) != 0) + *(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = cv_val.l; + else if ((flags & IS_S) != 0) + *(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = (short) cv_val.l; + else if ((flags & IS_C) != 0) + *(npos != 0 ? (signed char *) get_va_nth (argp, npos) : va_arg (arg, signed char *)) = (signed char) cv_val.ul; + else + *(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = (int) cv_val.l; + } + else + { + if ((flags & IS_LL) != 0) + *(npos != 0 ? (unsigned long long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long long *)) = cv_val.ull; + else if ((flags & IS_L) != 0) + *(npos != 0 ? (unsigned long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long *)) = cv_val.ul; + else if ((flags & IS_S) != 0) + *(npos != 0 ? (unsigned short *) get_va_nth (argp, npos) : va_arg (arg, unsigned short *)) + = (unsigned short) cv_val.ul; + else if ((flags & IS_C) != 0) + *(npos != 0 ? (unsigned char *) get_va_nth (argp, npos) : va_arg (arg, unsigned char *)) = (unsigned char) cv_val.ul; + else + *(npos != 0 ? (unsigned int *) get_va_nth (argp, npos) : va_arg (arg, unsigned int *)) = (unsigned int) cv_val.ul; + } + ++rval; + } + break; + + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + case 'a': case 'A': + if (width > 0) + --width; + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + seen_dot = seen_exp = 0; + is_neg = (c == '-' ? 1 : 0); + + if (c == '-' || c == '+') + { + if (width == 0 || (c = in_ch (s, &read_in)) == WEOF) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + } + + if (towlower (c) == 'n') + { + const wchar_t *match_txt = L"nan"; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + ++match_txt; + do + { + if (width == 0 || (c = in_ch (s, &read_in)) == WEOF + || towlower (c) != match_txt[0]) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + } + while (*match_txt != 0); + } + else if (towlower (c) == 'i') + { + const wchar_t *match_txt = L"inf"; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + ++match_txt; + do + { + if (width == 0 || (c = in_ch (s, &read_in)) == WEOF + || towlower (c) != match_txt[0]) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + } + while (*match_txt != 0); + + if (width != 0 && (c = in_ch (s, &read_in)) != WEOF && towlower (c) == 'i') + { + match_txt = L"inity"; + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + ++match_txt; + do + { + if (width == 0 || (c = in_ch (s, &read_in)) == WEOF + || towlower (c) != match_txt[0]) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + } + while (*match_txt != 0); + } + else if (width != 0 && c != WEOF) + back_ch (c, s, &read_in, 0); + } + else + { + not_in = 'e'; + if (width != 0 && c == '0') + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + c = in_ch (s, &read_in); + if (width > 0) + --width; + if (width != 0 && towlower (c) == 'x') + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + flags |= IS_HEX_FLOAT; + not_in = 'p'; + + flags &= ~USE_GROUP; + c = in_ch (s, &read_in); + if (width > 0) + --width; + } + } + + while (1) + { + if (iswdigit (c)) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + } + else if (!seen_exp && (flags & IS_HEX_FLOAT) != 0 && iswxdigit (c)) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + } + else if (seen_exp && wbuf[wbuf_cur_sz - 1] == not_in + && (c == '-' || c == '+')) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + } + else if (wbuf_cur_sz > 0 && !seen_exp + && (wchar_t) towlower (c) == not_in) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = not_in; + + seen_exp = seen_dot = 1; + } + else + { + if (!seen_dot && c == lc_decimal_point) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + seen_dot = 1; + } + else if ((flags & USE_GROUP) != 0 && !seen_dot && c == lc_thousands_sep) + { + /* As our conversion routines aren't supporting thousands + separators, we are filtering them here. */ + } + else + { + back_ch (c, s, &read_in, 0); + break; + } + } + + if (width == 0 || (c = in_ch (s, &read_in)) == WEOF) + break; + + if (width > 0) + --width; + } + + if (wbuf_cur_sz == 0 || ((flags & IS_HEX_FLOAT) != 0 && wbuf_cur_sz == 2)) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = 0; + + if ((flags & IS_LL) != 0) + { + long double d = __mingw_wcstold (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); + if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) + *(npos != 0 ? (long double *) get_va_nth (argp, npos) : va_arg (arg, long double *)) = is_neg ? -d : d; + } + else if ((flags & IS_L) != 0) + { + double d = __mingw_wcstod (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); + if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) + *(npos != 0 ? (double *) get_va_nth (argp, npos) : va_arg (arg, double *)) = is_neg ? -d : d; + } + else + { + float d = __mingw_wcstof (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); + if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) + *(npos != 0 ? (float *) get_va_nth (argp, npos) : va_arg (arg, float *)) = is_neg ? -d : d; + } + + if (wbuf == tmp_wbuf_ptr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + ++rval; + break; + + case '[': + if ((flags & IS_L) != 0) + { + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + str_sz = 100; + *pstr = (char *) malloc (100 * sizeof (wchar_t)); + if ((wstr = (wchar_t *) *pstr) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + wstr = (wchar_t *) get_va_nth (argp, npos); + else + wstr = va_arg (arg, wchar_t *); + if (!wstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + } + else if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + str_sz = 100; + if ((str = *pstr = (char *) malloc (100)) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + str = (char *) get_va_nth (argp, npos); + else + str = va_arg (arg, char *); + if (!str) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + not_in = (*f == '^' ? 1 : 0); + if (*f == '^') + f++; + + if (width < 0) + width = INT_MAX; + + tmp_wbuf_ptr = (wchar_t *) f; + + if (*f == L']') + ++f; + + while ((fc = *f++) != 0 && fc != L']'); + + if (fc == 0) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + temp_wbuf_end = (wchar_t *) f - 1; + + if ((flags & IS_L) != 0) + { + read_in_sv = read_in; + + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + do + { + int ended = 0; + for (wbuf_iter = tmp_wbuf_ptr; wbuf_iter < temp_wbuf_end;) + { + if (wbuf_iter[0] == '-' && wbuf_iter[1] != 0 + && (wbuf_iter + 1) != temp_wbuf_end + && wbuf_iter != tmp_wbuf_ptr + && (unsigned int) wbuf_iter[-1] <= (unsigned int) wbuf_iter[1]) + { + for (wc = wbuf_iter[-1] + 1; wc <= wbuf_iter[1] && (wint_t) wc != c; ++wc); + + if (wc <= wbuf_iter[1] && !not_in) + break; + if (wc <= wbuf_iter[1] && not_in) + { + back_ch (c, s, &read_in, 0); + ended = 1; + break; + } + + wbuf_iter += 2; + } + else + { + if ((wint_t) *wbuf_iter == c && !not_in) + break; + if ((wint_t) *wbuf_iter == c && not_in) + { + back_ch (c, s, &read_in, 0); + ended = 1; + break; + } + + ++wbuf_iter; + } + } + if (ended) + break; + + if (wbuf_iter == temp_wbuf_end && !not_in) + { + back_ch (c, s, &read_in, 0); + break; + } + + if ((flags & IS_SUPPRESSED) == 0) + { + *wstr++ = c; + + if ((flags & IS_ALLOC_USED) != 0 + && wstr == ((wchar_t *) *pstr + str_sz)) + { + new_sz = str_sz * 2; + while ((wstr = (wchar_t *) realloc (*pstr, + new_sz * sizeof (wchar_t))) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!wstr) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + ((wchar_t *) (*pstr))[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = (char *) wstr; + wstr += str_sz; + str_sz = new_sz; + } + } + } + while (--width > 0 && (c = in_ch (s, &read_in)) != WEOF); + + if (read_in_sv == read_in) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + *wstr++ = 0; + + optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); + pstr = NULL; + ++rval; + } + } + else + { + read_in_sv = read_in; + + if ((c = in_ch (s, &read_in)) == WEOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + memset (&state, 0, sizeof (state)); + + do + { + int ended = 0; + wbuf_iter = tmp_wbuf_ptr; + while (wbuf_iter < temp_wbuf_end) + { + if (wbuf_iter[0] == '-' && wbuf_iter[1] != 0 + && (wbuf_iter + 1) != temp_wbuf_end + && wbuf_iter != tmp_wbuf_ptr + && (unsigned int) wbuf_iter[-1] <= (unsigned int) wbuf_iter[1]) + { + for (wc = wbuf_iter[-1] + 1; wc <= wbuf_iter[1] && (wint_t) wc != c; ++wc); + + if (wc <= wbuf_iter[1] && !not_in) + break; + if (wc <= wbuf_iter[1] && not_in) + { + back_ch (c, s, &read_in, 0); + ended = 1; + break; + } + + wbuf_iter += 2; + } + else + { + if ((wint_t) *wbuf_iter == c && !not_in) + break; + if ((wint_t) *wbuf_iter == c && not_in) + { + back_ch (c, s, &read_in, 0); + ended = 1; + break; + } + + ++wbuf_iter; + } + } + + if (ended) + break; + if (wbuf_iter == temp_wbuf_end && !not_in) + { + back_ch (c, s, &read_in, 0); + break; + } + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0 + && (str + MB_CUR_MAX) >= (*pstr + str_sz)) + { + new_sz = str_sz * 2; + str_len = (str - *pstr); + + while ((nstr = (char *) realloc (*pstr, new_sz)) == NULL + && new_sz > (str_len + MB_CUR_MAX)) + new_sz = str_len + MB_CUR_MAX; + if (!nstr) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + ((*pstr))[str_len] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = nstr; + str = nstr + str_len; + str_sz = new_sz; + } + } + + n = wcrtomb ((flags & IS_SUPPRESSED) == 0 ? str : NULL, c, &state); + if (n == (size_t) -1LL) + { + errno = EILSEQ; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + str += n; + } + while (--width > 0 && (c = in_ch (s, &read_in)) != WEOF); + + if (read_in_sv == read_in) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + n = wcrtomb (buf, 0, &state); + if (n > 0 && (flags & IS_ALLOC_USED) != 0 + && (str + n) >= (*pstr + str_sz)) + { + str_len = (str - *pstr); + + if ((nstr = (char *) realloc (*pstr, str_len + n + 1)) == NULL) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + (*pstr)[str_len] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = nstr; + str = nstr + str_len; + str_sz = str_len + n + 1; + } + + if (n) + { + memcpy (str, buf, n); + str += n; + } + *str++ = 0; + + optimize_alloc (pstr, str, str_sz); + pstr = NULL; + ++rval; + } + } + break; + + default: + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if (ignore_ws) + { + while (iswspace ((c = in_ch (s, &read_in)))); + back_ch (c, s, &read_in, 0); + } + + return cleanup_return (rval, &gcollect, pstr, &wbuf); +} + +int +__mingw_vfwscanf (FILE *s, const wchar_t *format, va_list argp) +{ + _IFP ifp; + memset (&ifp, 0, sizeof (_IFP)); + ifp.fp = s; + return __mingw_swformat (&ifp, format, argp); +} + +int +__mingw_vswscanf (const wchar_t *s, const wchar_t *format, va_list argp) +{ + _IFP ifp; + memset (&ifp, 0, sizeof (_IFP)); + ifp.str = s; + ifp.is_string = 1; + return __mingw_swformat (&ifp, format, argp); +} +