From f2cb69fd8e2f29eb098fa16d03adef1031ea7728 Mon Sep 17 00:00:00 2001 From: Keith Marshall Date: Mon, 28 Jul 2008 23:24:20 +0000 Subject: [PATCH] Replace __mingw_snprintf() with new generic family implementation; likewise, replace __mingw_vsnprintf(). --- winsup/mingw/ChangeLog | 17 + winsup/mingw/mingwex/Makefile.in | 32 +- winsup/mingw/mingwex/gdtoa/mingw_snprintf.c | 1258 ---------- winsup/mingw/mingwex/stdio/pformat.c | 2511 +++++++++++++++++++ winsup/mingw/mingwex/stdio/pformat.h | 90 + winsup/mingw/mingwex/stdio/snprintf.c | 44 + winsup/mingw/mingwex/stdio/vsnprintf.c | 55 + 7 files changed, 2739 insertions(+), 1268 deletions(-) delete mode 100755 winsup/mingw/mingwex/gdtoa/mingw_snprintf.c create mode 100644 winsup/mingw/mingwex/stdio/pformat.c create mode 100644 winsup/mingw/mingwex/stdio/pformat.h create mode 100644 winsup/mingw/mingwex/stdio/snprintf.c create mode 100644 winsup/mingw/mingwex/stdio/vsnprintf.c diff --git a/winsup/mingw/ChangeLog b/winsup/mingw/ChangeLog index 57369a3c0..0107cdab2 100644 --- a/winsup/mingw/ChangeLog +++ b/winsup/mingw/ChangeLog @@ -1,3 +1,20 @@ +2008-07-29 Keith Marshall + + Replace __mingw_snprintf() with new generic family implementation; + likewise, replace __mingw_vsnprintf(). + + * mingwex/stdio/pformat.c mingwex/stdio/pformat.h: New files. + * mingwex/stdio/snprintf.c mingwex/stdio/vsnprintf.c: New files. + * mingwex/Makefile.in (STDIO_DISTFILES): Add them. + (GDTOA_DISTFILES): Remove mingw_snprintf.c + (STDIO_OBJS): Add pformat.o, snprintf.o and vsnprintf.o + (GDTOA_OBJS): Remove mingw_snprintf.o + (PFORMAT_CFLAGS): New macro; define it, as required by... + (pformat.o): ...this new explicit build target. + (snprintf.o, vsnprintf.o): Declare prerequisites. + + * mingwex/gdtoa/mingw_snprintf.c: Redundant file; delete it. + 2008-07-28 Keith Marshall Lay foundations for new printf() function family implementation. diff --git a/winsup/mingw/mingwex/Makefile.in b/winsup/mingw/mingwex/Makefile.in index 9563ad4e7..9142576a4 100644 --- a/winsup/mingw/mingwex/Makefile.in +++ b/winsup/mingw/mingwex/Makefile.in @@ -25,7 +25,9 @@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ mkinstalldirs = $(SHELL) $(srcdir)/../mkinstalldirs -DISTFILES = Makefile.in configure configure.in aclocal.m4 \ + +DISTFILES = \ + Makefile.in configure configure.in aclocal.m4 \ _Exit.c atoll.c dirent.c feclearexcept.c fegetenv.c \ fegetexceptflag.c fegetround.c feholdexcept.c feraiseexcept.c \ fesetenv.c fesetexceptflag.c fesetround.c fetestexcept.c \ @@ -37,8 +39,7 @@ DISTFILES = Makefile.in configure configure.in aclocal.m4 \ wdirent.c wmemchr.c wmemcmp.c wmemcpy.c wmemmove.c wmemset.c wtoll.c \ wcrtomb.c wctob.c mbrtowc.c btowc.c mb_wc_common.h \ gettimeofday.c isblank.c iswblank.c \ - basename.c dirname.c \ - usleep.c \ + basename.c dirname.c usleep.c \ tsearch.c twalk.c tdelete.c tfind.c MATH_DISTFILES = \ @@ -67,12 +68,13 @@ MATH_DISTFILES = \ signbit.c signbitf.c signbitl.c sinf.S sinhf.c sinhl.c sinl.S \ sqrtf.c sqrtl.c tanf.S tanhf.c tanhl.c tanl.S tgamma.c \ tgammaf.c tgammal.c trunc.c truncf.c truncl.c \ - acosh.c acoshf.c acoshl.c asinh.c asinhf.c asinhl.c \ + acosh.c acoshf.c acoshl.c asinh.c asinhf.c asinhl.c \ atanh.c atanhf.c atanhl.c fastmath.h STDIO_DISTFILES = \ fopen64.c fseeko64.c ftello64.c lseek64.c \ vfscanf.c vfwscanf.c vscanf.c vsscanf.c vswscanf.c vwscanf.c \ + pformat.c pformat.h snprintf.c vsnprintf.c \ snwprintf.c vsnwprintf.c COMPLEX_DISTFILES = \ @@ -90,7 +92,8 @@ GDTOA_DISTFILES = \ arithchk.c dmisc.c dtoa.c g__fmt.c g_dfmt.c g_ffmt.c g_xfmt.c \ gd_arith.h gd_qnan.h gdtoa.c gdtoa.h gdtoaimp.h gethex.c gmisc.c \ hd_init.c hexnan.c misc.c qnan.c README smisc.c strtodg.c strtodnrp.c \ - strtof.c strtopx.c sum.c ulp.c mingw_snprintf.c + strtof.c strtopx.c sum.c ulp.c + CC = @CC@ # FIXME: Which is it, CC or CC_FOR_TARGET? CC_FOR_TARGET = $(CC) @@ -139,7 +142,7 @@ STDLIB_STUB_OBJS = \ STDIO_OBJS = \ fopen64.o fseeko64.o ftello64.o lseek64.o \ vfscanf.o vfwscanf.o vscanf.o vsscanf.o vswscanf.o vwscanf.o \ - snwprintf.o vsnwprintf.o + pformat.o snprintf.o vsnprintf.o snwprintf.o vsnwprintf.o MATH_OBJS = \ acosf.o acosl.o asinf.o asinl.o atan2f.o atan2l.o \ atanf.o atanl.o cbrt.o cbrtf.o cbrtl.o ceilf.o ceill.o \ @@ -175,8 +178,7 @@ FENV_OBJS = fesetround.o fegetround.o \ feclearexcept.o feholdexcept.o fegetexceptflag.o \ feraiseexcept.o fetestexcept.o fesetexceptflag.o POSIX_OBJS = \ - dirent.o wdirent.o getopt.o ftruncate.o gettimeofday.o \ - usleep.o \ + dirent.o wdirent.o getopt.o ftruncate.o gettimeofday.o usleep.o \ basename.o dirname.o tsearch.o twalk.o tdelete.o tfind.o REPLACE_OBJS = \ mingw-aligned-malloc.o mingw-fseek.o @@ -193,8 +195,7 @@ COMPLEX_OBJS = \ GDTOA_OBJS = \ dmisc.o dtoa.o g__fmt.o g_dfmt.o g_ffmt.o g_xfmt.o gdtoa.o \ gethex.o gmisc.o hd_init.o hexnan.o misc.o smisc.o \ - strtodg.o strtodnrp.o strtof.o strtopx.o sum.o ulp.o \ - mingw_snprintf.o + strtodg.o strtodnrp.o strtof.o strtopx.o sum.o ulp.o LIB_OBJS = $(Q8_OBJS) $(CTYPE_OBJS) $(STDLIB_STUB_OBJS) \ $(STDIO_OBJS) $(MATH_OBJS) $(MATH_ROUND_OBJS) \ @@ -252,6 +253,14 @@ distclean: .s.o: $(CC) -c $< -o $@ +# pformat.o needs an explicit build rule; we always build it +# assuming __MSVCRT_VERSION__ >= 0x0800, relying on ofmt_stub.s +# (in the parent directory) to maintain forward compatibility +# for earlier versions of MSVCRT.DLL +# +PFORMAT_CFLAGS = $(ALL_CFLAGS) -I $(srcdir)/gdtoa -D__MSVCRT_VERSION__=0x0800 +pformat.o: pformat.c pformat.h + $(CC) -c $(PFORMAT_CFLAGS) $(srcdir)/stdio/$*.c -o $@ # # Dependencies @@ -267,6 +276,9 @@ $(GDTOA_OBJS): gd_arith.h gdtoa.h gdtoaimp.h gd_qnan.h $(MATH_ROUND_OBJS) $(MATH_LROUND_OBJS) $(MATH_LLROUND_OBJS): round_internal.h +snprintf.o: snprintf.c pformat.h +vsnprintf.o: vsnprintf.c pformat.h + dist: mkdir $(distdir)/mingwex chmod 755 $(distdir)/mingwex diff --git a/winsup/mingw/mingwex/gdtoa/mingw_snprintf.c b/winsup/mingw/mingwex/gdtoa/mingw_snprintf.c deleted file mode 100755 index 26f4573e8..000000000 --- a/winsup/mingw/mingwex/gdtoa/mingw_snprintf.c +++ /dev/null @@ -1,1258 +0,0 @@ -/**************************************************************** -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. -****************************************************************/ - -/* This implements most of ANSI C's printf, fprintf, and sprintf, - * with %.0g and %.0G giving the shortest decimal string - * that rounds to the number being converted, and with negative - * precisions allowed for %f. - */ - -/* - * Extracted from the AMPL solvers library module - * http://www.netlib.org/ampl/solvers/printf.c - * and modified for use in libmingwex.a. - * - * libstdc++ amd libgfortran expect an snprintf that can handle host - * widest float type. This one handle 80 bit long double. Printing - * to streams using this alternative implementation is not yet - * supported, - * - * Danny Smith - * 2007-06-01 - */ - - -#ifdef KR_headers -#include "varargs.h" -#else -#include "stddef.h" -#include "stdarg.h" -#include "stdlib.h" -#endif - -#include -#include "string.h" -#include "errno.h" - -#ifdef KR_headers -#define Const /* const */ -#define Voidptr char* -#ifndef size_t__ -#define size_t int -#define size_t__ -#endif - -#else - -#define Const const -#define Voidptr void* - -#endif - - - -#ifdef USE_FILE_OUTPUT -#undef MESS -#ifndef Stderr -#define Stderr stderr -#endif - -#ifdef _windows_ -#undef PF_BUF -#define MESS -#include "mux0.h" -#define stdout_or_err(f) (f == stdout) -#else -#define stdout_or_err(f) (f == Stderr || f == stdout) -#endif - -#endif /* USE_FILE_OUTPUT */ - -#include -#include -#include "gdtoaimp.h" -#ifdef USE_LOCALE -#include "locale.h" -#endif - -/* - * For a MinGW build, we provide the implementation dependent entries - * `__mingw_snprintf' and `__mingw_vsnprintf', then alias them to provide - * the C99 conforming implementations of `snprintf()' and `vsnprintf()'. - */ -# define Snprintf __mingw_snprintf -# define Vsnprintf __mingw_vsnprintf - -int __cdecl __MINGW_NOTHROW -snprintf(char *, size_t, const char *, ...) __attribute__((alias("__mingw_snprintf"))); -int __cdecl __MINGW_NOTHROW -vsnprintf (char *, size_t, const char *, __VALIST) __attribute__((alias("__mingw_vsnprintf"))); - - -static char* __ldtoa (long double ld, int mode, int ndig, int *decpt, - int *sign, char **rve) -{ - - static FPI fpi = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, 0 }; - ULong bits[2]; - int ex, kind; - int fptype = __fpclassifyl (ld); - union - { - unsigned short L[6]; - long double ld; - } u; - - u.ld = ld; - - *sign = u.L[4] & 0x8000; - ex = u.L[4] & 0x7fff; - - bits[1] = (u.L[3] << 16) | u.L[2]; - bits[0] = (u.L[1] << 16) | u.L[0]; - - if (fptype & FP_NAN) /* NaN or Inf */ - { - if (fptype & FP_NORMAL) - kind = STRTOG_Infinite; - else - kind = STRTOG_NaN; - } - else if (fptype & FP_NORMAL) /* Normal or subnormal */ - { - if (fptype & FP_ZERO) - { - kind = STRTOG_Denormal; - ex = 1; - } - else - kind = STRTOG_Normal; - - ex -= 0x3fff + 63; - } - else - kind = STRTOG_Zero; - - return __gdtoa (&fpi, ex, bits, &kind, mode, ndig, decpt, rve); -} - -#ifdef USE_ULDIV -/* This is for avoiding 64-bit divisions on the DEC Alpha, since */ -/* they are not portable among variants of OSF1 (DEC's Unix). */ - -#define ULDIV(a,b) uldiv_ASL(a,(unsigned long)(b)) - -#ifndef LLBITS -#define LLBITS 6 -#endif -#ifndef ULONG -#define ULONG unsigned long -#endif - - static int -klog(ULONG x) -{ - int k, rv = 0; - - if (x > 1L) - for(k = 1 << LLBITS-1;;) { - if (x >= (1L << k)) { - rv |= k; - x >>= k; - } - if (!(k >>= 1)) - break; - } - return rv; - } - - ULONG -uldiv_ASL(ULONG a, ULONG b) -{ - int ka; - ULONG c, k; - static ULONG b0; - static int kb; - - if (a < b) - return 0; - if (b != b0) { - b0 = b; - kb = klog(b); - } - k = 1; - if ((ka = klog(a) - kb) > 0) { - k <<= ka; - b <<= ka; - } - c = 0; - for(;;) { - if (a >= b) { - a -= b; - c |= k; - } - if (!(k >>= 1)) - break; - a <<= 1; - } - return c; - } - -#else -#define ULDIV(a,b) a / b -#endif /* USE_ULDIV */ - - typedef struct -Finfo { - union { - #ifdef USE_FILE_OUTPUT - FILE *cf; - #endif - char *sf; - } u; - char *ob0, *obe1; - size_t lastlen; - } Finfo; - - typedef char *(*Putfunc) ANSI((Finfo*, int*)); - -#ifdef USE_FILE_OUTPUT -#ifdef PF_BUF -FILE *stderr_ASL = (FILE*)&stderr_ASL; -void (*pfbuf_print_ASL) ANSI((char*)); -char *pfbuf_ASL; -static char *pfbuf_next; -static size_t pfbuf_len; -extern Char *mymalloc_ASL ANSI((size_t)); -extern Char *myralloc_ASL ANSI((void *, size_t)); - -#undef fflush -#ifdef old_fflush_ASL -#define fflush old_fflush_ASL -#endif - - void -fflush_ASL(FILE *f) -{ - if (f == stderr_ASL) { - if (pfbuf_ASL && pfbuf_print_ASL) { - (*pfbuf_print_ASL)(pfbuf_ASL); - free(pfbuf_ASL); - pfbuf_ASL = 0; - } - } - else - fflush(f); - } - - static void -pf_put(char *buf, int len) -{ - size_t x, y; - if (!pfbuf_ASL) { - x = len + 256; - if (x < 512) - x = 512; - pfbuf_ASL = pfbuf_next = (char*)mymalloc_ASL(pfbuf_len = x); - } - else if ((y = (pfbuf_next - pfbuf_ASL) + len) >= pfbuf_len) { - x = pfbuf_len; - while((x <<= 1) <= y); - y = pfbuf_next - pfbuf_ASL; - pfbuf_ASL = (char*)myralloc_ASL(pfbuf_ASL, x); - pfbuf_next = pfbuf_ASL + y; - pfbuf_len = x; - } - memcpy(pfbuf_next, buf, len); - pfbuf_next += len; - *pfbuf_next = 0; - } - - static char * -pfput(Finfo *f, int *rvp) -{ - int n; - char *ob0 = f->ob0; - *rvp += n = (int)(f->obe1 - ob0); - pf_put(ob0, n); - return ob0; - } -#endif /* PF_BUF */ - - static char * -Fput -#ifdef KR_headers - (f, rvp) register Finfo *f; int *rvp; -#else - (register Finfo *f, int *rvp) -#endif -{ - register char *ob0 = f->ob0; - - *rvp += f->obe1 - ob0; - *f->obe1 = 0; - fputs(ob0, f->u.cf); - return ob0; - } - - -#ifdef _windows_ -int stdout_fileno_ASL = 1; - - static char * -Wput -#ifdef KR_headers - (f, rvp) register Finfo *f; int *rvp; -#else - (register Finfo *f, int *rvp) -#endif -{ - register char *ob0 = f->ob0; - - *rvp += f->obe1 - ob0; - *f->obe1 = 0; - mwrite(ob0, f->obe1 - ob0); - return ob0; - } -#endif /*_windows_*/ -#endif /* USE_FILE_OUTPUT */ - -#ifndef INT_IS_LONG -#if defined (__SIZEOF_LONG__) && defined (__SIZEOF_INT__) \ - && (__SIZEOF_LONG__) == (__SIZEOF_INT__) -#define INT_IS_LONG 1 -#endif -#endif - -#define put(x) { *outbuf++ = x; if (outbuf == obe) outbuf = (*fput)(f,&rv); } - - static int -x_sprintf -#ifdef KR_headers - (obe, fput, f, fmt, ap) - char *obe, *fmt; Finfo *f; Putfunc fput; va_list ap; -#else - (char *obe, Putfunc fput, Finfo *f, const char *fmt, va_list ap) -#endif -{ - char *digits, *ob0, *outbuf, *s, *s0, *se; - Const char *fmt0; - char buf[32]; - long long i = 0; - unsigned long long j; - unsigned long long u = 0; - - double x; - long double xx; - int flag_ld = 0; - int alt, base, c, decpt, dot, conv, i1, lead0, left, - prec, prec1, psign, rv, sgn, sign, width; - enum { - LEN_I, - LEN_L, - LEN_S, - LEN_LL - } len; - long long Ltmp; - intptr_t ip; - short sh; - long k; - unsigned short us; - unsigned long ui; - static char hex[] = "0123456789abcdef"; - static char Hex[] = "0123456789ABCDEF"; - -#ifdef USE_LOCALE - char decimalpoint = *localeconv()->decimal_point; -#else - static const char decimalpoint = '.'; -#endif - - ob0 = outbuf = f->ob0; - rv = 0; - for(;;) { - for(;;) { - switch(c = *fmt++) { - case 0: - goto done; - case '%': - break; - default: - put(c) - continue; - } - break; - } - alt=dot=lead0=left=prec=psign=sign=width=0; - len = LEN_I; - fmt0 = fmt; - fmtloop: - switch(conv = *fmt++) { - case ' ': - case '+': - sign = conv; - goto fmtloop; - case '-': - if (dot) - psign = 1; - else - left = 1; - goto fmtloop; - case '#': - alt = 1; - goto fmtloop; - case '0': - if (!lead0 && !dot) { - lead0 = 1; - goto fmtloop; - } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - k = conv - '0'; - while((c = *fmt) >= '0' && c <= '9') { - k = 10*k + c - '0'; - fmt++; - } - if (dot) - prec = psign ? -k : k; - else - width = k; - goto fmtloop; - case 'h': - len = LEN_S; - goto fmtloop; -#ifndef NO_MSVC_EXTENSIONS - case 'I': - if (fmt[0] == '3' && fmt[1] == '2') - { - fmt += 2; - len = LEN_L; - } - else if (fmt[0] == '6' && fmt[1] == '4') - { - fmt += 2; - len = LEN_LL; - } - else - len = sizeof (intptr_t) == sizeof (long long) - ? LEN_LL : LEN_L; - goto fmtloop; -#endif - case 'l': - if (fmt[0] == 'l') - { - fmt++; - len = LEN_LL; - } - else - len = LEN_L; - goto fmtloop; - case 'L': - flag_ld++; - goto fmtloop; - case '.': - dot = 1; - lead0 = 0; - goto fmtloop; - case '*': - k = va_arg(ap, int); - if (dot) - prec = k; - else { - if (k < 0) { - sign = '-'; - k = -k; - } - width = k; - } - goto fmtloop; - case 'c': -/* %lc (for wctomb conversion) is not implemented. */ - c = va_arg(ap, int); - put(c) - continue; - case '%': - put(conv) - continue; - case 'u': - switch(len) { - case LEN_I: -#if !INT_IS_LONG - ui = va_arg(ap, int); - i = ui; - break; -#endif - case LEN_L: - ui = va_arg(ap, long); - i = ui; - break; - case LEN_S: - us = va_arg(ap, long); - i = us; - break; - case LEN_LL: - i = va_arg(ap, long long); - } - sign = 0; - goto have_i; - case 'i': - case 'd': - switch(len) { - case LEN_I: -#if !INT_IS_LONG - k = va_arg(ap, int); - i = k; - break; -#endif - case LEN_L: - k = va_arg(ap, long); - i = k; - break; - case LEN_S: - sh = va_arg(ap, long); - i = sh; - break; - case LEN_LL: - i = va_arg(ap, long long); - - } - if (i < 0) { - sign = '-'; - i = -i; - } - have_i: - base = 10; - u = i; - digits = hex; - baseloop: - s = buf; - if (!u) - alt = 0; - do { - j = ULDIV(u, base); - *s++ = digits[u - base * j]; - } - while((u = j)); - prec -= c = s - buf; - if (alt && conv == 'o' && prec <= 0) - prec = 1; - if ((width -= c) > 0) { - if (prec > 0) - width -= prec; - if (sign) - width--; - if (alt == 2) - width--; - } - if (left) { - if (alt == 2) - put('0') /* for 0x */ - if (sign) - put(sign) - while(--prec >= 0) - put('0') - do put(*--s) - while(s > buf); - while(--width >= 0) - put(' ') - continue; - } - if (width > 0) { - if (lead0) { - if (alt == 2) - put('0') - if (sign) - put(sign) - while(--width >= 0) - put('0') - goto s_loop; - } - else - while(--width >= 0) - put(' ') - } - if (alt == 2) - put('0') - if (sign) - put(sign) - s_loop: - while(--prec >= 0) - put('0') - do put(*--s) - while(s > buf); - continue; - case 'n': - ip = va_arg(ap, intptr_t); - if (!ip) - ip = (intptr_t) &Ltmp; - c = outbuf - ob0 + rv; - switch(len) { - case LEN_I: -#if !INT_IS_LONG - *(int*)ip = c; - break; -#endif - case LEN_L: - *(long*)ip = c; - break; - case LEN_S: - *(short*)ip = c; - break; - case LEN_LL: - *(long long*) ip = c; - break; - } - break; - case 'p': - alt = 1; - len = sizeof (intptr_t) == sizeof (long long) - ? LEN_LL : LEN_L; - /* no break */ - case 'x': - digits = hex; - goto more_x; - case 'X': - digits = Hex; - more_x: - if (alt) { - alt = 2; - sign = conv; - } - else - sign = 0; - base = 16; - get_u: - switch(len) { - case LEN_I: -#if !INT_IS_LONG - ui = va_arg(ap, int); - u = ui; - break; -#endif - case LEN_L: - ui = va_arg(ap, long); - u = ui; - break; - case LEN_S: - us = va_arg(ap, long); - u = us; - break; - case LEN_LL: - u = va_arg(ap, long long); - } - if (!u) - sign = alt = 0; - goto baseloop; - case 'o': - base = 8; - digits = hex; - goto get_u; - case 's': -/* %ls (for wctombs conversion) is not implemented. */ - s0 = 0; - s = va_arg(ap, char*); - if (!s) - s = ""; - if (prec < 0) - prec = 0; - have_s: - if (dot) { - for(c = 0; c < prec; c++) - if (!s[c]) - break; - prec = c; - } - else - prec = strlen(s); - width -= prec; - if (!left) - while(--width >= 0) - put(' ') - while(--prec >= 0) - put(*s++) - while(--width >= 0) - put(' ') - if (s0) - __freedtoa(s0); - continue; - case 'f': - if (!dot) - prec = 6; - if (flag_ld) - xx = va_arg(ap, long double); - else - { - x = va_arg(ap, double); - xx = x; - } - - s = s0 = __ldtoa(xx, 3, prec, &decpt, &sgn, &se); - if (decpt == -32768) { - fmt9999: - dot = prec = alt = 0; - if (*s == 'N' || *s == 'I') - goto have_s; - decpt = strlen(s); - } - f_fmt: - if (sgn && (xx||sign)) - sign = '-'; - if (prec > 0) - width -= prec; - if (width > 0) { - if (sign) - --width; - if (decpt <= 0) { - --width; - if (prec > 0) - --width; - } - else { - if (s == se) - decpt = 1; - width -= decpt; - if (prec > 0 || alt) - --width; - } - } - if (width > 0 && !left) { - if (lead0) { - if (sign) - put(sign) - sign = 0; - do put('0') - while(--width > 0); - } - else do put(' ') - while(--width > 0); - } - if (sign) - put(sign) - if (decpt <= 0) { - put('0') - if (prec > 0 || alt) - put(decimalpoint) - while(decpt < 0) { - put('0') - prec--; - decpt++; - } - } - else { - do { - if ((c = *s)) - s++; - else - c = '0'; - put(c) - } - while(--decpt > 0); - if (prec > 0 || alt) - put(decimalpoint) - } - while(--prec >= 0) { - if ((c = *s)) - s++; - else - c = '0'; - put(c) - } - while(--width >= 0) - put(' ') - __freedtoa(s0); - continue; - case 'G': - case 'g': - if (!dot) - prec = 6; - if (flag_ld) - xx = va_arg(ap, long double); - else - { - x = va_arg(ap, double); - xx = x; - } - if (prec < 0) - prec = 0; - s = s0 = __ldtoa(xx, prec ? 2 : 0, prec, &decpt, - &sgn, &se); - if (decpt == -32768) - goto fmt9999; - c = se - s; - prec1 = prec; - if (!prec) { - prec = c; - prec1 = c + (s[1] || alt ? 5 : 4); - /* %.0g gives 10 rather than 1e1 */ - } - if (decpt > -4 && decpt <= prec1) { - if (alt) - prec -= decpt; - else - prec = c - decpt; - if (prec < 0) - prec = 0; - goto f_fmt; - } - conv -= 2; - if (!alt && prec > c) - prec = c; - --prec; - goto e_fmt; - case 'e': - case 'E': - if (!dot) - prec = 6; - if (flag_ld) - xx = va_arg(ap, long double); - else - { - x = va_arg(ap, double); - xx = x; - } - if (prec < 0) - prec = 0; - s = s0 = __ldtoa(xx, 2, prec + 1, &decpt, - &sgn, &se); - if (decpt == -32768) - goto fmt9999; - e_fmt: - if (sgn && (xx||sign)) - sign = '-'; - if ((width -= prec + 5) > 0) { - if (sign) - --width; - if (prec || alt) - --width; - } - - if ((c = --decpt) < 0) - c = -c; - - while(c >= 100) { - --width; - c /= 10; - } - if (width > 0 && !left) { - if (lead0) { - if (sign) - put(sign) - sign = 0; - do put('0') - while(--width > 0); - } - else do put(' ') - while(--width > 0); - } - if (sign) - put(sign) - put(*s++) - if (prec || alt) - put(decimalpoint) - while(--prec >= 0) { - if ((c = *s)) - s++; - else - c = '0'; - put(c) - } - put(conv) - if (decpt < 0) { - put('-') - decpt = -decpt; - } - else - put('+') - for(c = 2, k = 10; 10 * k <= decpt; c++, k *= 10); - for(;;) { - i1 = decpt / k; - put(i1 + '0') - if (--c <= 0) - break; - decpt -= i1*k; - decpt *= 10; - } - while(--width >= 0) - put(' ') - __freedtoa(s0); - continue; - default: - put('%') - while(fmt0 < fmt) - put(*fmt0++) - continue; - } - } - done: - *outbuf = 0; - return (f->lastlen = outbuf - ob0) + rv; - } - -#define Bsize 256 -#ifdef USE_FILE_OUTPUT - int -Printf -#ifdef KR_headers - (va_alist) - va_dcl -{ - char *fmt; - - va_list ap; - int rv; - Finfo f; - char buf[Bsize]; - - va_start(ap); - fmt = va_arg(ap, char*); - /*}*/ -#else - (const char *fmt, ...) -{ - va_list ap; - int rv; - Finfo f; - char buf[Bsize]; - - va_start(ap, fmt); -#endif - f.u.cf = stdout; - f.ob0 = buf; - f.obe1 = buf + Bsize - 1; -#ifdef _windows_ - if (fileno(stdout) == stdout_fileno_ASL) { - rv = x_sprintf(f.obe1, Wput, &f, fmt, ap); - mwrite(buf, f.lastlen); - } - else -#endif -#ifdef PF_BUF - if (stdout == stderr_ASL) { - rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); - pf_put(buf, f.lastlen); - } - else -#endif /* PF_BUF */ - { - rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); - fputs(buf, stdout); - } - va_end(ap); - return rv; - } - - static char * -Sput -#ifdef KR_headers - (f, rvp) Finfo *f; int *rvp; -#else - (Finfo *f, int *rvp) -#endif -{ - if (Printf("\nBUG! Sput called!\n", f, rvp)) - /* pass vp, rvp and return 0 to shut diagnostics off */ - exit(250); - return 0; - } - - int -Sprintf -#ifdef KR_headers - (va_alist) - va_dcl -{ - char *s, *fmt; - va_list ap; - int rv; - Finfo f; - - va_start(ap); - s = va_arg(ap, char*); - fmt = va_arg(ap, char*); - /*}*/ -#else - (char *s, const char *fmt, ...) -{ - va_list ap; - int rv; - Finfo f; - - va_start(ap, fmt); -#endif - f.ob0 = s; - rv = x_sprintf(s, Sput, &f, fmt, ap); - va_end(ap); - return rv; - } - -int -Fprintf -#ifdef KR_headers - (va_alist) - va_dcl -{ - FILE *F; - char *s, *fmt; - va_list ap; - int rv; - Finfo f; - char buf[Bsize]; - - va_start(ap); - F = va_arg(ap, FILE*); - fmt = va_arg(ap, char*); - /*}*/ -#else - (FILE *F, const char *fmt, ...) -{ - va_list ap; - int rv; - Finfo f; - char buf[Bsize]; - - va_start(ap, fmt); -#endif - f.u.cf = F; - f.ob0 = buf; - f.obe1 = buf + Bsize - 1; -#ifdef MESS - if (stdout_or_err(F)) { -#ifdef _windows_ - if (fileno(stdout) == stdout_fileno_ASL) { - rv = x_sprintf(f.obe1, Wput, &f, fmt, ap); - mwrite(buf, f.lastlen); - } - else -#endif -#ifdef PF_BUF - if (F == stderr_ASL) { - rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); - pf_put(buf, f.lastlen); - } - else -#endif - { - rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); - fputs(buf, F); - } - } - else -#endif /*MESS*/ - { -#ifdef PF_BUF - if (F == stderr_ASL) { - rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); - pf_put(buf, f.lastlen); - } - else -#endif - { - rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); - fputs(buf, F); - } - } - va_end(ap); - return rv; - } - - int -Vsprintf -#ifdef KR_headers - (s, fmt, ap) char *s, *fmt; va_list ap; -#else - (char *s, const char *fmt, va_list ap) -#endif -{ - Finfo f; - return x_sprintf(f.ob0 = s, Sput, &f, fmt, ap); - } - - int -Vfprintf -#ifdef KR_headers - (F, fmt, ap) FILE *F; char *fmt; va_list ap; -#else - (FILE *F, const char *fmt, va_list ap) -#endif -{ - char buf[Bsize]; - int rv; - Finfo f; - - f.u.cf = F; - f.ob0 = buf; - f.obe1 = buf + Bsize - 1; -#ifdef MESS - if (stdout_or_err(F)) { -#ifdef _windows_ - if (fileno(stdout) == stdout_fileno_ASL) { - rv = x_sprintf(f.obe1, Wput, &f, fmt, ap); - mwrite(buf, f.lastlen); - } - else -#endif -#ifdef PF_BUF - if (F == stderr_ASL) { - rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); - pf_put(buf, f.lastlen); - } - else -#endif - { - rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); - fputs(buf, F); - } - } - else -#endif /*MESS*/ - { -#ifdef PF_BUF - if (F == stderr_ASL) { - rv = x_sprintf(f.obe1, pfput, &f, fmt, ap); - pf_put(buf, f.lastlen); - } - else -#endif - { - rv = x_sprintf(f.obe1, Fput, &f, fmt, ap); - fputs(buf, F); - } - } - va_end(ap); - return rv; - } - - void -Perror -#ifdef KR_headers - (s) char *s; -#else - (const char *s) -#endif -{ - if (s && *s) - fprintf(Stderr, "%s: ", s); - fprintf(Stderr, "%s\n", strerror(errno)); - } -#endif /* USE_FILE_OUTPUT */ - - - static char * -Snput -#ifdef KR_headers - (f, rvp) Finfo *f; int *rvp; -#else - (Finfo *f, int *rvp) -#endif -{ - char *s, *s0; - size_t L; - - *rvp += Bsize; - s0 = f->ob0; - s = f->u.sf; - if ((L = f->obe1 - s) > Bsize) { - L = Bsize; - goto copy; - } - if (L > 0) { - copy: - memcpy(s, s0, L); - f->u.sf = s + L; - } - return s0; - } - - - int -Vsnprintf -#ifdef KR_headers - (s, n, fmt, ap) char *s; size_t n; char *fmt; va_list ap; -#else - (char *s, size_t n, const char *fmt, va_list ap) -#endif -{ - Finfo f; - char buf[Bsize]; - int L, rv; - - if (n <= 0 || !s) { - n = 1; - s = buf; - } - f.u.sf = s; - f.ob0 = buf; - f.obe1 = s + n - 1; - rv = x_sprintf(buf + Bsize, Snput, &f, fmt, ap); - if (f.lastlen > (L = f.obe1 - f.u.sf)) - f.lastlen = L; - if (f.lastlen > 0) { - memcpy(f.u.sf, buf, f.lastlen); - f.u.sf += f.lastlen; - } - *f.u.sf = 0; - return rv; - } - int -Snprintf -#ifdef KR_headers - (va_alist) - va_dcl -{ - char *s, *fmt; - int rv; - size_t n; - va_list ap; - - va_start(ap); - s = va_arg(ap, char*); - n = va_arg(ap, size_t); - fmt = va_arg(ap, char*); - /*}*/ -#else - (char *s, size_t n, const char *fmt, ...) -{ - int rv; - va_list ap; - - va_start(ap, fmt); -#endif - rv = Vsnprintf(s, n, fmt, ap); - va_end(ap); - return rv; - } - - -#if (EXPORT_WEAK_SNPRINTF_ALIAS) -int __cdecl snprintf(char*, size_t, const char* , ...) __attribute__ ((weak, alias ("__mingw_snprintf"))); -int __cdecl vsnprintf (char*, size_t n, const char*, __gnuc_va_list) __attribute__ ((weak, alias ("__mingw_vsnprintf"))); -#endif - diff --git a/winsup/mingw/mingwex/stdio/pformat.c b/winsup/mingw/mingwex/stdio/pformat.c new file mode 100644 index 000000000..82dbcd741 --- /dev/null +++ b/winsup/mingw/mingwex/stdio/pformat.c @@ -0,0 +1,2511 @@ +/* FIXME: to be removed one day; for now we explicitly are not + * prepared to support the POSIX-XSI additions to the C99 standard. + */ +#undef WITH_XSI_FEATURES + +/* pformat.c + * + * $Id$ + * + * 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. + ****************************************************************** + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* FIXME: The following belongs in values.h, but current MinGW + * has nothing useful there! OTOH, values.h is not a standard + * header, and it's use may be considered obsolete; perhaps it + * is better to just keep these definitions here. + */ +#ifndef _VALUES_H +/* + * values.h + * + */ +#define _VALUES_H + +#include + +#define _TYPEBITS(type) (sizeof(type) * CHAR_BIT) + +#define LLONGBITS _TYPEBITS(long long) + +#endif /* !defined _VALUES_H -- end of file */ + +#include "pformat.h" + +/* Bit-map constants, defining the internal format control + * states, which propagate through the flags. + */ +#define PFORMAT_HASHED 0x0800 +#define PFORMAT_LJUSTIFY 0x0400 +#define PFORMAT_ZEROFILL 0x0200 + +#define PFORMAT_JUSTIFY (PFORMAT_LJUSTIFY | PFORMAT_ZEROFILL) +#define PFORMAT_IGNORE -1 + +#define PFORMAT_SIGNED 0x01C0 +#define PFORMAT_POSITIVE 0x0100 +#define PFORMAT_NEGATIVE 0x0080 +#define PFORMAT_ADDSPACE 0x0040 + +#define PFORMAT_XCASE 0x0020 + +#define PFORMAT_LDOUBLE 0x0004 + +/* `%o' format digit extraction mask, and shift count... + * (These are constant, and do not propagate through the flags). + */ +#define PFORMAT_OMASK 0x0007 +#define PFORMAT_OSHIFT 0x0003 + +/* `%x' and `%X' format digit extraction mask, and shift count... + * (These are constant, and do not propagate through the flags). + */ +#define PFORMAT_XMASK 0x000F +#define PFORMAT_XSHIFT 0x0004 + +/* 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 + +#ifdef _WIN32 +/* + * The Microsoft standard for printing `%e' format exponents is + * with a minimum of three digits, unless explicitly set otherwise, + * by a prior invocation of the `_set_output_format()' function. + * + * The following macro allows us to replicate this behaviour. + */ +# define PFORMAT_MINEXP __pformat_exponent_digits() + /* + * However, this feature is unsupported for versions of the + * MSVC runtime library prior to msvcr80.dll, and by default, + * MinGW uses an earlier version, (equivalent to msvcr60.dll), + * for which `_TWO_DIGIT_EXPONENT' will be undefined. + */ +# ifndef _TWO_DIGIT_EXPONENT + /* + * This hack works around the lack of the `_set_output_format()' + * feature, when supporting versions of the MSVC runtime library + * prior to msvcr80.dll; it simply enforces Microsoft's original + * convention, for all cases where the feature is unsupported. + */ +# define _get_output_format() 0 +# define _TWO_DIGIT_EXPONENT 1 +# endif +/* + * Irrespective of the MSVCRT version supported, *we* will add + * an additional capability, through the following inline function, + * which will allow the user to choose his own preferred default + * for `PRINTF_EXPONENT_DIGITS', through the simple expedient + * of defining it as an environment variable. + */ +static __inline__ __attribute__((__always_inline__)) +int __pformat_exponent_digits( void ) +{ + char *exponent_digits = getenv( "PRINTF_EXPONENT_DIGITS" ); + return ((exponent_digits != NULL) && ((unsigned)(*exponent_digits - '0') < 3)) + || (_get_output_format() & _TWO_DIGIT_EXPONENT) + ? 2 + : 3 + ; +} +#else +/* + * When we don't care to mimic Microsoft's standard behaviour, + * we adopt the C99/POSIX standard of two digit exponents. + */ +# define PFORMAT_MINEXP 2 +#endif + +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; +} __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_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. + */ +#define __pformat_arg_length( type ) \ + 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 + +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 count; + int quota; + int expmin; +} __pformat_t; + +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... + */ + ((char *)(stream->dest))[stream->count] = c; + } + ++stream->count; +} + +static +void __pformat_putchars( const char *s, int count, __pformat_t *stream ) +{ + /* 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 ); +} + +static __inline__ +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. + */ + __pformat_putchars( s, strlen( s ), stream ); +} + +static +void __pformat_wputchars( const wchar_t *s, int count, __pformat_t *stream ) +{ + /* 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 ); +} + +static __inline__ __attribute__((__always_inline__)) +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. + */ + __pformat_wputchars( s, wcslen( s ), stream ); +} + +static __inline__ +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; + 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. + */ + char buf[__pformat_int_bufsiz(1, PFORMAT_OSHIFT, stream)]; + char *p = buf; int precision; + + if( stream->flags & PFORMAT_NEGATIVE ) + { + /* 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; + } + + /* Encode the input value for display... + */ + 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. + */ + *p++ = '0' + (unsigned char)(value.__pformat_ullong_t % 10LL); + value.__pformat_ullong_t /= 10LL; + } + + 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 zeroes. + */ + 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 mask = (fmt == 'o') ? PFORMAT_OMASK : PFORMAT_XMASK; + int shift = (fmt == 'o') ? PFORMAT_OSHIFT : PFORMAT_XSHIFT; + char buf[__pformat_int_bufsiz(2, shift, stream)]; + char *p = buf; + + 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; + } + + 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 zeroes to + * consume the remaining field width. + */ + while( width-- > 0 ) + *p++ = '0'; + + if( (fmt != 'o') && (stream->flags & PFORMAT_HASHED) ) + { + /* For formats other than octal, the PFORMAT_HASHED output style + * requires the addition of a two character radix indicator, as a + * prefix to the actual encoded numeric value. + */ + *p++ = fmt; + *p++ = '0'; + } + + if( (width > 0) && ((stream->flags & PFORMAT_LJUSTIFY) == 0) ) + /* + * When not doing flush left justification, (i.e. the `-' flag + * is not set), any residual unreserved field width must appear + * as blank padding, to the left of the output value. + */ + while( width-- > 0 ) + __pformat_putc( '\x20', stream ); + + while( p > buf ) + /* + * Move the queued output from the local buffer to the ultimate + * destination, in LIFO order. + */ + __pformat_putc( *--p, stream ); + + /* If we still haven't consumed the entire specified field width, + * we must be doing flush left justification; any residual width + * must be filled with blanks, to the right of the output value. + */ + while( width-- > 0 ) + __pformat_putc( '\x20', stream ); +} + +typedef union +{ + /* A multifaceted representation of an IEEE extended precision, + * (80-bit), floating point number, facilitating access to its + * component parts. + */ + double __pformat_fpreg_double_t; + long double __pformat_fpreg_ldouble_t; + struct + { unsigned long long __pformat_fpreg_mantissa; + signed short __pformat_fpreg_exponent; + }; + unsigned short __pformat_fpreg_bitmap[5]; + unsigned long __pformat_fpreg_bits; +} __pformat_fpreg_t; + +#ifdef _WIN32 +/* TODO: make this unconditional in final release... + * (see note at head of associated `#else' block. + */ +#include "gdtoa.h" + +static +char *__pformat_cvt( int mode, __pformat_fpreg_t x, int nd, int *dp, int *sign ) +{ + /* Helper function, derived from David M. Gay's `g_xfmt()', calling + * his `__gdtoa()' function in a manner to provide extended precision + * replacements for `ecvt()' and `fcvt()'. + */ + unsigned int k, e = 0; char *ep; + static FPI fpi = { 64, 1-16383-64+1, 32766-16383-64+1, FPI_Round_near, 0 }; + + /* Classify the argument into an appropriate `__gdtoa()' category... + */ + if( (k = __fpclassifyl( x.__pformat_fpreg_ldouble_t )) & 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 __inline__ __attribute__((__always_inline__)) +char *__pformat_ecvt( long double x, int precision, int *dp, int *sign ) +{ + /* A convenience wrapper for the above... + * it emulates `ecvt()', but takes a `long double' argument. + */ + __pformat_fpreg_t z; z.__pformat_fpreg_ldouble_t = x; + return __pformat_cvt( 2, z, precision, dp, sign ); +} + +static __inline__ __attribute__((__always_inline__)) +char *__pformat_fcvt( long double x, int precision, int *dp, int *sign ) +{ + /* A convenience wrapper for the above... + * it emulates `fcvt()', but takes a `long double' argument. + */ + __pformat_fpreg_t z; z.__pformat_fpreg_ldouble_t = x; + return __pformat_cvt( 3, z, precision, dp, sign ); +} + +/* The following are required, to clean up the `__gdtoa()' memory pool, + * after processing the data returned by the above. + */ +#define __pformat_ecvt_release( value ) __freedtoa( value ) +#define __pformat_fcvt_release( value ) __freedtoa( value ) + +#else +/* + * TODO: remove this before final release; it is included here as a + * convenience for testing, without requiring a working `__gdtoa()'. + */ +static __inline__ +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 __inline__ +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 __inline__ +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... + */ + 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 ); + } + + else + /* No localisation: just use ASCII '.'... + */ + __pformat_putc( '.', stream ); +} + +static __inline__ __attribute__((__always_inline__)) +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 + /* 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 zeroes + * 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--; + + /* 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 zeroes. + */ + while( len-- > 0 ) + __pformat_putc( *value ? *value++ : '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 ); + } + + 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 ); + __pformat_emit_radix_point( stream ); + + /* The radix point offset, `len', may be negative; this implies + * that additional zeroes must appear, following the radix point, + * and preceding the first significant digit. We reduce the + * precision, (adding a negative value), to allow for these + * additional zeroes, then emit the zeroes as required. + */ + stream->precision += len; + while( len++ < 0 ) + __pformat_putc( '0', stream ); + } + + /* Now we emit any remaining significant digits, or trailing zeroes, + * 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. + */ + 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 ); +} + +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 zeroes... + */ + if( ((stream->precision = strlen( value ) - intlen) < 0) + /* + * This may require a compensating adjustment to the field + * width, to accommodate significant trailing zeroes, 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 zeroes + * 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 zeroes... + */ + 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], *p = buf; + __pformat_intarg_t exponent; short exp_width = 2; + + /* The mantissa field of the argument value representation can + * accommodate at most 16 hexadecimal digits, of which one will + * be placed before the radix point, leaving at most 15 digits + * to satisfy any requested precision; thus... + */ + if( (stream->precision >= 0) && (stream->precision < 15) ) + { + /* When the user specifies a precision within this range, + * we want to adjust the mantissa, to retain just the number + * of digits required, rounding up when the high bit of the + * leftmost discarded digit is set; (mask of 0x08 accounts + * for exactly one digit discarded, shifting 4 bits per + * digit, with up to 14 additional digits, to consume the + * full availability of 15 precision digits). + * + * However, before we perform the rounding operation, we + * normalise the mantissa, shifting it to the left by as many + * bit positions may be necessary, until its highest order bit + * is set, thus preserving the maximum number of bits in the + * rounded result as possible. + */ + while( value.__pformat_fpreg_mantissa < (LLONG_MAX + 1ULL) ) + value.__pformat_fpreg_mantissa <<= 1; + + /* We then shift the mantissa one bit position back to the + * right, to guard against possible overflow when the rounding + * adjustment is added. + */ + value.__pformat_fpreg_mantissa >>= 1; + + /* We now add the rounding adjustment, noting that to keep the + * 0x08 mask aligned with the shifted mantissa, we also need to + * shift it right by one bit initially, changing its starting + * value to 0x04... + */ + value.__pformat_fpreg_mantissa += 0x04LL << (4 * (14 - stream->precision)); + if( (value.__pformat_fpreg_mantissa & (LLONG_MAX + 1ULL)) == 0ULL ) + /* + * When the rounding adjustment would not have overflowed, + * then we shift back to the left again, to fill the vacated + * bit we reserved to accommodate the carry. + */ + value.__pformat_fpreg_mantissa <<= 1; + + else + /* Otherwise the rounding adjustment would have overflowed, + * so the carry has already filled the vacated bit; the effect + * of this is equivalent to an increment of the exponent. + */ + value.__pformat_fpreg_exponent++; + + /* We now complete the rounding to the required precision, by + * shifting the unwanted digits out, from the right hand end of + * the mantissa. + */ + value.__pformat_fpreg_mantissa >>= 4 * (15 - stream->precision); + } + + /* Encode the significant digits of the mantissa in hexadecimal + * ASCII notation, ready for transfer to the output stream... + */ + while( value.__pformat_fpreg_mantissa ) + { + /* taking the rightmost digit in each pass... + */ + int c = value.__pformat_fpreg_mantissa & 0xF; + if( c == value.__pformat_fpreg_mantissa ) + { + /* inserting the radix point, when we reach the last, + * (i.e. the most significant digit), unless we found no + * less significant digits, with no mandatory radix point + * inclusion, and no additional required precision... + */ + if( (p > buf) + || (stream->flags & PFORMAT_HASHED) || (stream->precision > 0) ) + /* + * Internally, we represent the radix point as an ASCII '.'; + * we will replace it with any locale specific alternative, + * at the time of transfer to the ultimate destination. + */ + *p++ = '.'; + + /* If the most significant hexadecimal digit of the encoded + * output value is greater than one, then the indicated value + * will appear too large, by an additional binary exponent + * corresponding to the number of higher order bit positions + * which it occupies... + */ + while( value.__pformat_fpreg_mantissa > 1 ) + { + /* so reduce the exponent value to compensate... + */ + value.__pformat_fpreg_exponent--; + value.__pformat_fpreg_mantissa >>= 1; + } + } + + else if( stream->precision > 0 ) + /* + * we have not yet fulfilled the desired precision, + * and we have not yet found the most significant digit, + * so account for the current digit, within the field + * width required to meet the specified precision. + */ + stream->precision--; + + if( (c > 0) || (p > buf) || (stream->precision >= 0) ) + /* + * Ignoring insignificant trailing zeroes, (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 exponent = 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( (exponent = exponent / 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 zeroes 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; + exponent.__pformat_llong_t = value.__pformat_fpreg_exponent; + __pformat_int( exponent, stream ); +} + +static +void __pformat_xdouble( double x, __pformat_t *stream ) +{ + /* Handler for `%a' and `%A' format specifiers, (with argument + * value specified as `double' type). + */ + unsigned sign_bit = 0; + __pformat_fpreg_t z; z.__pformat_fpreg_double_t = x; + + /* First check for NaN; it is emitted unsigned... + */ + if( isnan( x ) ) + __pformat_emit_inf_or_nan( sign_bit, "NaN", stream ); + + else + { /* Capture the sign bit up-front, so we can show it correctly + * even when the argument value is zero or infinite. + */ + if( (sign_bit = (z.__pformat_fpreg_bitmap[3] & 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... + * first move its exponent into the appropriate field... + */ + z.__pformat_fpreg_bitmap[4] = (z.__pformat_fpreg_bitmap[3] >> 4) & 0x7FF; + + /* Realign the mantissa, leaving space for a + * normalised most significant digit... + */ + z.__pformat_fpreg_mantissa <<= 8; + z.__pformat_fpreg_bitmap[3] = (z.__pformat_fpreg_bitmap[3] & 0x0FFF); + + /* Check for zero value... + */ + if( z.__pformat_fpreg_exponent || z.__pformat_fpreg_mantissa ) + { + /* and only when the value is non-zero, + * eliminate the bias from the exponent... + */ + z.__pformat_fpreg_exponent -= 0x3FF; + + /* Check for a possible denormalised value... + */ + if( z.__pformat_fpreg_exponent > -126 ) + /* + * and normalise when it isn't. + */ + z.__pformat_fpreg_bitmap[3] += 0x1000; + } + + /* Finally, hand the adjusted representation off to the generalised + * hexadecimal floating point format handler... + */ + __pformat_emit_xfloat( z, stream ); + } + } +} + +static +void __pformat_xldouble( long double x, __pformat_t *stream ) +{ + /* Handler for `%La' and `%LA' format specifiers, (with argument + * value specified as `long double' type). + */ + unsigned sign_bit = 0; + __pformat_fpreg_t z; z.__pformat_fpreg_ldouble_t = x; + + /* First check for NaN; it is emitted unsigned... + */ + if( isnan( x ) ) + __pformat_emit_inf_or_nan( sign_bit, "NaN", stream ); + + else + { /* Capture the sign bit up-front, so we can show it correctly + * even when the argument value is zero or infinite. + */ + if( (sign_bit = (z.__pformat_fpreg_exponent & 0x8000)) != 0 ) + stream->flags |= PFORMAT_NEGATIVE; + + /* Check for infinity, (positive or negative)... + */ + if( isinf( x ) ) + /* + * displaying the appropriately signed indicator, + * when appropriate. + */ + __pformat_emit_inf_or_nan( sign_bit, "Inf", stream ); + + else + { /* The argument value is a representable number... + * extract the effective value of the biased exponent... + */ + z.__pformat_fpreg_exponent &= 0x7FFF; + if( z.__pformat_fpreg_exponent || z.__pformat_fpreg_mantissa ) + /* + * and if the argument value itself is non-zero, + * eliminate the bias from the exponent... + */ + z.__pformat_fpreg_exponent -= 0x3FFF; + + /* Finally, hand the adjusted representation off to the + * generalised hexadecimal floating point format handler... + */ + __pformat_emit_xfloat( z, stream ); + } + } +} + +int __pformat( int flags, void *dest, int max, const char *fmt, va_list argv ) +{ + int c; + + __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, /* zero output char count */ + max, /* establish output limit */ + PFORMAT_MINEXP /* exponent chars preferred */ + }; + + 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; + + /* Restart capture for dynamic field width and precision specs... + */ + int *width_spec = &stream.width; + + /* 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). + */ + __pformat_putc( c, &stream ); + goto format_scan; + + case 'C': + /* + * Equivalent to `%lc'; set `length' accordingly, + * and simply fall through. + */ + length = PFORMAT_LENGTH_LONG; + + case 'c': + /* + * Single, (or single multibyte), character output... + * + * We handle these by copying the argument into our local + * `argval' buffer, and then we pass the address of that to + * either `__pformat_putchars()' or `__pformat_wputchars()', + * as appropriate, effectively formatting it as a string of + * the appropriate type, with a length of one. + * + * A side effect of this method of handling character data + * is that, if the user sets a precision of zero, then no + * character is actually emitted; we don't want that, so we + * forcibly override any user specified precision. + */ + stream.precision = PFORMAT_IGNORE; + + /* Now we invoke the appropriate format handler... + */ + if( (length == PFORMAT_LENGTH_LONG) + || (length == PFORMAT_LENGTH_LLONG) ) + { + /* considering any `long' type modifier as a reference to + * `wchar_t' data, (which is promoted to an `int' argument)... + */ + argval.__pformat_ullong_t = (wchar_t)(va_arg( argv, int )); + __pformat_wputchars( (wchar_t *)(&argval), 1, &stream ); + } + + else + { /* while anything else is simply taken as `char', (which + * is also promoted to an `int' argument)... + */ + argval.__pformat_uchar_t = (unsigned char)(va_arg( argv, int )); + __pformat_putchars( (char *)(&argval), 1, &stream ); + } + goto format_scan; + + case 'S': + /* + * Equivalent to `%ls'; set `length' accordingly, + * and simply fall through. + */ + length = PFORMAT_LENGTH_LONG; + + case 's': + if( (length == PFORMAT_LENGTH_LONG) + || (length == PFORMAT_LENGTH_LLONG) ) + { + /* considering any `long' type modifier as a reference to + * a `wchar_t' string... + */ + __pformat_wcputs( va_arg( argv, wchar_t * ), &stream ); + } + + else + /* This is normal string output; + * we simply invoke the appropriate handler... + */ + __pformat_puts( va_arg( argv, char * ), &stream ); + + goto format_scan; + + case 'o': + case 'u': + case 'x': + case 'X': + /* + * Unsigned integer values; octal, decimal or hexadecimal format... + */ + 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( length == PFORMAT_LENGTH_LLONG ) + /* + * The argument is a `long long' type... + */ + argval.__pformat_llong_t = va_arg( argv, long long ); + + else if( length == PFORMAT_LENGTH_LONG ) + /* + * or here, a `long' type... + */ + argval.__pformat_llong_t = va_arg( argv, long ); + + else + { /* otherwise, it's an `int' type... + */ + argval.__pformat_llong_t = va_arg( argv, int ); + if( length == PFORMAT_LENGTH_SHORT ) + /* + * but it was promoted from a `short' type... + */ + argval.__pformat_llong_t = argval.__pformat_short_t; + else if( length == PFORMAT_LENGTH_CHAR ) + /* + * or even from a `char' type... + */ + argval.__pformat_llong_t = argval.__pformat_char_t; + } + + /* In any case, all share a common handler... + */ + __pformat_int( argval, &stream ); + goto format_scan; + + case 'p': + /* + * Pointer argument; format as hexadecimal, with `0x' prefix... + */ + stream.flags |= PFORMAT_HASHED; + argval.__pformat_ullong_t = va_arg( argv, uintptr_t ); + __pformat_xint( 'x', argval, &stream ); + goto format_scan; + + case 'e': + /* + * Floating point format, with lower case exponent indicator + * and lower case `inf' or `nan' representation when required; + * select lower case mode, and simply fall through... + */ + stream.flags |= PFORMAT_XCASE; + + case 'E': + /* + * Floating point format, with upper case exponent indicator + * and upper case `INF' or `NAN' representation when required, + * (or lower case for all of these, on fall through from above); + * select lower case mode, and simply fall through... + */ + if( stream.flags & PFORMAT_LDOUBLE ) + /* + * for a `long double' argument... + */ + __pformat_efloat( va_arg( argv, long double ), &stream ); + + else + /* or just a `double', which we promote to `long double', + * so the two may share a common format handler. + */ + __pformat_efloat( (long double)(va_arg( argv, double )), &stream ); + + goto format_scan; + + case 'f': + /* + * Fixed point format, using lower case for `inf' and + * `nan', when appropriate; select lower case mode, and + * simply fall through... + */ + stream.flags |= PFORMAT_XCASE; + + case 'F': + /* + * Fixed case format using upper case, or lower case on + * fall through from above, for `INF' and `NAN'... + */ + if( stream.flags & PFORMAT_LDOUBLE ) + /* + * for a `long double' argument... + */ + __pformat_float( va_arg( argv, long double ), &stream ); + + else + /* or just a `double', which we promote to `long double', + * so the two may share a common format handler. + */ + __pformat_float( (long double)(va_arg( argv, double )), &stream ); + + goto format_scan; + + case 'g': + /* + * Generalised floating point format, with lower case + * exponent indicator when required; select lower case + * mode, and simply fall through... + */ + stream.flags |= PFORMAT_XCASE; + + case 'G': + /* + * Generalised floating point format, with upper case, + * or on fall through from above, with lower case exponent + * indicator when required... + */ + if( stream.flags & PFORMAT_LDOUBLE ) + /* + * for a `long double' argument... + */ + __pformat_gfloat( va_arg( argv, long double ), &stream ); + + else + /* or just a `double', which we promote to `long double', + * so the two may share a common format handler. + */ + __pformat_gfloat( (long double)(va_arg( argv, double )), &stream ); + + goto format_scan; + + case 'a': + /* + * Hexadecimal floating point format, with lower case radix + * and exponent indicators; select the lower case mode, and + * fall through... + */ + stream.flags |= PFORMAT_XCASE; + + case 'A': + /* + * Hexadecimal floating point format; handles radix and + * exponent indicators in either upper or lower case... + */ + if( stream.flags & PFORMAT_LDOUBLE ) + /* + * with a `long double' argument... + */ + __pformat_xldouble( va_arg( argv, long double ), &stream ); + + else + /* or just a `double'. + */ + __pformat_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... + */ + 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 + + 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; + +# ifndef _WIN32 + /* + * Microsoft's MSVCRT implementation also uses `l' + * as a modifier for `long double'; if we don't want + * to support that, we end this case here... + */ + state = PFORMAT_END; + break; + + /* otherwise, we simply fall through... + */ +# endif + + 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; + +# ifdef WITH_XSI_FEATURES + + 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; + break; + +# endif + + 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 zeroes' + * option, or it may simply be a digit in a width or in a + * precision specification... + */ + if( state == PFORMAT_INIT ) + { + /* This is the flag usage... + */ + stream.flags |= PFORMAT_ZEROFILL; + break; + } + + default: + /* + * If we didn't match anything above, then we will check + * for digits, which we may accumulate to generate field + * width or precision specifications... + */ + if( (state < PFORMAT_END) && ('9' >= c) && (c >= '0') ) + { + if( state == PFORMAT_INIT ) + /* + * Initial digits explicitly relate to field width... + */ + state = PFORMAT_SET_WIDTH; + + else if( state == PFORMAT_GET_PRECISION ) + /* + * while those following a precision indicator + * explicitly relate to precision. + */ + state = PFORMAT_SET_PRECISION; + + if( width_spec ) + { + /* We are accepting a width or precision specification... + */ + if( *width_spec < 0 ) + /* + * and accumulation hasn't started yet; we simply + * initialise the accumulator with the current digit + * value, converting from ASCII to decimal. + */ + *width_spec = c - '0'; + + else + /* Accumulation has already started; we perform a + * `leftwise decimal digit shift' on the accumulator, + * (i.e. multiply it by ten), then add the decimal + * equivalent value of the current digit. + */ + *width_spec = *width_spec * 10 + c - '0'; + } + } + + else + /* We found a digit out of context, or some other character + * with no designated meaning; silently reject it, and any + * further characters other than argument length modifiers, + * until this format specification is completely resolved. + */ + state = PFORMAT_END; + } + } + } + + else + /* We just parsed a character which is not included within any format + * specification; we simply emit it as a literal. + */ + __pformat_putc( c, &stream ); + } + + /* When we have fully dispatched the format string, the return value is the + * total number of bytes we transferred to the output destination. + */ + return stream.count; +} + +/* $RCSfile$Revision$: end of file */ diff --git a/winsup/mingw/mingwex/stdio/pformat.h b/winsup/mingw/mingwex/stdio/pformat.h new file mode 100644 index 000000000..a2ace696c --- /dev/null +++ b/winsup/mingw/mingwex/stdio/pformat.h @@ -0,0 +1,90 @@ +#ifndef PFORMAT_H +/* + * pformat.h + * + * $Id$ + * + * 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 + +/* This isn't currently defined therein, + * but is a potential candidate for inclusion in _mingw.h + */ +#ifdef __MINGW32__ +# define __stringify__(NAME) #NAME +# define __mingw_quoted(NAME) __stringify__(__mingw_##NAME) +# define __mingw_alias(NAME) __attribute__((alias(__mingw_quoted(NAME)))) NAME +#else +# define __mingw_alias(NAME) NAME +#endif + +/* The following are the declarations specific to the `pformat' API... + */ +#define PFORMAT_TO_FILE 0x1000 +#define PFORMAT_NOLIMIT 0x2000 + +#ifdef __MINGW32__ + /* + * Map MinGW specific function names, for use in place of the generic + * implementation defined equivalent function names. + */ +# define __pformat __mingw_pformat + +# 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 + +int __cdecl __pformat( int, void *, int, const char *, va_list ) __MINGW_NOTHROW; + +#endif /* !defined PFORMAT_H: $RCSfile$Revision$: end of file */ diff --git a/winsup/mingw/mingwex/stdio/snprintf.c b/winsup/mingw/mingwex/stdio/snprintf.c new file mode 100644 index 000000000..4d022e7dc --- /dev/null +++ b/winsup/mingw/mingwex/stdio/snprintf.c @@ -0,0 +1,44 @@ +/* snprintf.c + * + * $Id$ + * + * 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 "pformat.h" + +int __cdecl __snprintf (char *, size_t, const char *fmt, ...) __MINGW_NOTHROW; +int __cdecl __mingw_alias(snprintf) (char *, size_t, const char *, ...) __MINGW_NOTHROW; + +int __cdecl __vsnprintf (char *, size_t, const char *fmt, va_list) __MINGW_NOTHROW; + +int __cdecl __snprintf( char *buf, size_t length, const char *fmt, ... ) +{ + va_list argv; va_start( argv, fmt ); + register int retval = __vsnprintf( buf, length, fmt, argv ); + va_end( argv ); + return retval; +} + +/* $RCSfile$Revision$: end of file */ diff --git a/winsup/mingw/mingwex/stdio/vsnprintf.c b/winsup/mingw/mingwex/stdio/vsnprintf.c new file mode 100644 index 000000000..a9722c235 --- /dev/null +++ b/winsup/mingw/mingwex/stdio/vsnprintf.c @@ -0,0 +1,55 @@ +/* vsnprintf.c + * + * $Id$ + * + * 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 "pformat.h" + +int __cdecl __vsnprintf (char *, size_t, const char *fmt, va_list) __MINGW_NOTHROW; +int __cdecl __mingw_alias(vsnprintf) (char *, size_t, const char *, va_list) __MINGW_NOTHROW; + +int __cdecl __vsnprintf( char *buf, size_t length, const char *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 < length ? retval : length] = '\0'; + + return retval; +} + +/* $RCSfile$Revision$: end of file */