#define HAVE_ALLOCA 1
#include <libioP.h>
#include <alloca.h>
#include <ctype.h>
#include <float.h>
#include <gmp-mparam.h>
#include <gmp.h>
#include <ieee754.h>
#include <stdlib/gmp-impl.h>
#include <stdlib/longlong.h>
#include <stdlib/fpioconst.h>
#include <locale/localeinfo.h>
#include <limits.h>
#include <math.h>
#include <printf.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <wchar.h>
#include <stdbool.h>
#include <rounding-mode.h>
#ifdef COMPILE_WPRINTF
# define CHAR_T wchar_t
#else
# define CHAR_T char
#endif
#include "_i18n_number.h"
#ifndef NDEBUG
# define NDEBUG
#endif
#include <assert.h>
#define PUT(f, s, n) _IO_sputn (f, s, n)
#define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
#undef putc
#define putc(c, f) (wide \
? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
#define size_t _IO_size_t
#define FILE _IO_FILE
#define outchar(ch) \
do \
{ \
const int outc = (ch); \
if (putc (outc, fp) == EOF) \
{ \
if (buffer_malloced) \
free (wbuffer); \
return -1; \
} \
++done; \
} while (0)
#define PRINT(ptr, wptr, len) \
do \
{ \
size_t outlen = (len); \
if (len > 20) \
{ \
if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen) \
{ \
if (buffer_malloced) \
free (wbuffer); \
return -1; \
} \
ptr += outlen; \
done += outlen; \
} \
else \
{ \
if (wide) \
while (outlen-- > 0) \
outchar (*wptr++); \
else \
while (outlen-- > 0) \
outchar (*ptr++); \
} \
} while (0)
#define PADN(ch, len) \
do \
{ \
if (PAD (fp, ch, len) != len) \
{ \
if (buffer_malloced) \
free (wbuffer); \
return -1; \
} \
done += len; \
} \
while (0)
#define MPN_VAR(name) mp_limb_t *name; mp_size_t name##size
#define MPN_ASSIGN(dst,src) \
memcpy (dst, src, (dst##size = src##size) * sizeof (mp_limb_t))
#define MPN_GE(u,v) \
(u##size > v##size || (u##size == v##size && __mpn_cmp (u, v, u##size) >= 0))
extern mp_size_t __mpn_extract_double (mp_ptr res_ptr, mp_size_t size,
int *expt, int *is_neg,
double value);
extern mp_size_t __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size,
int *expt, int *is_neg,
long double value);
extern unsigned int __guess_grouping (unsigned int intdig_max,
const char *grouping);
static wchar_t *group_number (wchar_t *buf, wchar_t *bufend,
unsigned int intdig_no, const char *grouping,
wchar_t thousands_sep, int ngroups)
internal_function;
struct hack_digit_param
{
int expsign;
int type;
int exponent;
MPN_VAR(frac);
MPN_VAR(scale);
MPN_VAR(tmp);
};
static wchar_t
hack_digit (struct hack_digit_param *p)
{
mp_limb_t hi;
mp_limb_t _cy;
if (p->expsign != 0 && p->type == 'f' && p->exponent-- > 0)
hi = 0;
else if (p->scalesize == 0)
{
hi = p->frac[p->fracsize - 1];
p->frac[p->fracsize - 1] = __mpn_mul_1 (p->frac, p->frac,
p->fracsize - 1, 10);
}
else
{
if (p->fracsize < p->scalesize)
hi = 0;
else
{
hi = mpn_divmod (p->tmp, p->frac, p->fracsize,
p->scale, p->scalesize);
p->tmp[p->fracsize - p->scalesize] = hi;
hi = p->tmp[0];
p->fracsize = p->scalesize;
while (p->fracsize != 0 && p->frac[p->fracsize - 1] == 0)
--p->fracsize;
if (p->fracsize == 0)
{
p->fracsize = 1;
return L'0' + hi;
}
}
_cy = __mpn_mul_1 (p->frac, p->frac, p->fracsize, 10);
if (_cy != 0)
p->frac[p->fracsize++] = _cy;
}
return L'0' + hi;
}
int
__printf_fp (FILE *fp,
const struct printf_info *info,
const void *const *args)
{
union
{
double dbl;
__long_double_t ldbl;
}
fpnum;
const char *decimal;
wchar_t decimalwc;
const char *thousands_sep = NULL;
wchar_t thousands_sepwc = 0;
const char *grouping;
const char *special = NULL;
const wchar_t *wspecial = NULL;
mp_limb_t fp_input[(LDBL_MANT_DIG + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB];
int to_shift = 0;
struct hack_digit_param p;
int is_neg = 0;
int done = 0;
mp_limb_t cy;
int wide = info->wide;
wchar_t *wbuffer = NULL;
int buffer_malloced = 0;
p.expsign = 0;
if (info->extra == 0)
{
decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
decimalwc = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
}
else
{
decimal = _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
if (*decimal == '\0')
decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
decimalwc = _NL_CURRENT_WORD (LC_MONETARY,
_NL_MONETARY_DECIMAL_POINT_WC);
if (decimalwc == L'\0')
decimalwc = _NL_CURRENT_WORD (LC_NUMERIC,
_NL_NUMERIC_DECIMAL_POINT_WC);
}
assert (*decimal != '\0');
assert (decimalwc != L'\0');
if (info->group)
{
if (info->extra == 0)
grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
else
grouping = _NL_CURRENT (LC_MONETARY, MON_GROUPING);
if (*grouping <= 0 || *grouping == CHAR_MAX)
grouping = NULL;
else
{
if (wide)
{
if (info->extra == 0)
thousands_sepwc =
_NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);
else
thousands_sepwc =
_NL_CURRENT_WORD (LC_MONETARY,
_NL_MONETARY_THOUSANDS_SEP_WC);
}
else
{
if (info->extra == 0)
thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
else
thousands_sep = _NL_CURRENT (LC_MONETARY, MON_THOUSANDS_SEP);
}
if ((wide && thousands_sepwc == L'\0')
|| (! wide && *thousands_sep == '\0'))
grouping = NULL;
else if (thousands_sepwc == L'\0')
thousands_sepwc = 0xfffffffe;
}
}
else
grouping = NULL;
#ifndef __NO_LONG_DOUBLE_MATH
if (info->is_long_double && sizeof (long double) > sizeof (double))
{
int res;
fpnum.ldbl = *(const long double *) args[0];
if (isnan (fpnum.ldbl))
{
is_neg = signbit (fpnum.ldbl);
if (isupper (info->spec))
{
special = "NAN";
wspecial = L"NAN";
}
else
{
special = "nan";
wspecial = L"nan";
}
}
else if ((res = isinf (fpnum.ldbl)))
{
is_neg = res < 0;
if (isupper (info->spec))
{
special = "INF";
wspecial = L"INF";
}
else
{
special = "inf";
wspecial = L"inf";
}
}
else
{
p.fracsize = __mpn_extract_long_double (fp_input,
(sizeof (fp_input) /
sizeof (fp_input[0])),
&p.exponent, &is_neg,
fpnum.ldbl);
to_shift = 1 + p.fracsize * BITS_PER_MP_LIMB - LDBL_MANT_DIG;
}
}
else
#endif
{
int res;
fpnum.dbl = *(const double *) args[0];
if (isnan (fpnum.dbl))
{
union ieee754_double u = { .d = fpnum.dbl };
is_neg = u.ieee.negative != 0;
if (isupper (info->spec))
{
special = "NAN";
wspecial = L"NAN";
}
else
{
special = "nan";
wspecial = L"nan";
}
}
else if ((res = isinf (fpnum.dbl)))
{
is_neg = res < 0;
if (isupper (info->spec))
{
special = "INF";
wspecial = L"INF";
}
else
{
special = "inf";
wspecial = L"inf";
}
}
else
{
p.fracsize = __mpn_extract_double (fp_input,
(sizeof (fp_input)
/ sizeof (fp_input[0])),
&p.exponent, &is_neg, fpnum.dbl);
to_shift = 1 + p.fracsize * BITS_PER_MP_LIMB - DBL_MANT_DIG;
}
}
if (special)
{
int width = info->width;
if (is_neg || info->showsign || info->space)
--width;
width -= 3;
if (!info->left && width > 0)
PADN (' ', width);
if (is_neg)
outchar ('-');
else if (info->showsign)
outchar ('+');
else if (info->space)
outchar (' ');
PRINT (special, wspecial, 3);
if (info->left && width > 0)
PADN (' ', width);
return done;
}
{
mp_size_t bignum_size = ((ABS (p.exponent) + BITS_PER_MP_LIMB - 1)
/ BITS_PER_MP_LIMB
+ (LDBL_MANT_DIG / BITS_PER_MP_LIMB > 2 ? 8 : 4))
* sizeof (mp_limb_t);
p.frac = (mp_limb_t *) alloca (bignum_size);
p.tmp = (mp_limb_t *) alloca (bignum_size);
p.scale = (mp_limb_t *) alloca (bignum_size);
}
p.scalesize = 0;
if (p.exponent > 2)
{
int scaleexpo = 0;
int explog = LDBL_MAX_10_EXP_LOG;
int exp10 = 0;
const struct mp_power *powers = &_fpioconst_pow10[explog + 1];
int cnt_h, cnt_l, i;
if ((p.exponent + to_shift) % BITS_PER_MP_LIMB == 0)
{
MPN_COPY_DECR (p.frac + (p.exponent + to_shift) / BITS_PER_MP_LIMB,
fp_input, p.fracsize);
p.fracsize += (p.exponent + to_shift) / BITS_PER_MP_LIMB;
}
else
{
cy = __mpn_lshift (p.frac +
(p.exponent + to_shift) / BITS_PER_MP_LIMB,
fp_input, p.fracsize,
(p.exponent + to_shift) % BITS_PER_MP_LIMB);
p.fracsize += (p.exponent + to_shift) / BITS_PER_MP_LIMB;
if (cy)
p.frac[p.fracsize++] = cy;
}
MPN_ZERO (p.frac, (p.exponent + to_shift) / BITS_PER_MP_LIMB);
assert (powers > &_fpioconst_pow10[0]);
do
{
--powers;
if (p.exponent >= scaleexpo + powers->p_expo - 1)
{
if (p.scalesize == 0)
{
#ifndef __NO_LONG_DOUBLE_MATH
if (LDBL_MANT_DIG > _FPIO_CONST_OFFSET * BITS_PER_MP_LIMB
&& info->is_long_double)
{
#define _FPIO_CONST_SHIFT \
(((LDBL_MANT_DIG + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB) \
- _FPIO_CONST_OFFSET)
p.tmpsize = powers->arraysize + _FPIO_CONST_SHIFT;
memcpy (p.tmp + _FPIO_CONST_SHIFT,
&__tens[powers->arrayoff],
p.tmpsize * sizeof (mp_limb_t));
MPN_ZERO (p.tmp, _FPIO_CONST_SHIFT);
p.exponent += _FPIO_CONST_SHIFT * BITS_PER_MP_LIMB;
}
else
#endif
{
p.tmpsize = powers->arraysize;
memcpy (p.tmp, &__tens[powers->arrayoff],
p.tmpsize * sizeof (mp_limb_t));
}
}
else
{
cy = __mpn_mul (p.tmp, p.scale, p.scalesize,
&__tens[powers->arrayoff
+ _FPIO_CONST_OFFSET],
powers->arraysize - _FPIO_CONST_OFFSET);
p.tmpsize = p.scalesize +
powers->arraysize - _FPIO_CONST_OFFSET;
if (cy == 0)
--p.tmpsize;
}
if (MPN_GE (p.frac, p.tmp))
{
int cnt;
MPN_ASSIGN (p.scale, p.tmp);
count_leading_zeros (cnt, p.scale[p.scalesize - 1]);
scaleexpo = (p.scalesize - 2) * BITS_PER_MP_LIMB - cnt - 1;
exp10 |= 1 << explog;
}
}
--explog;
}
while (powers > &_fpioconst_pow10[0]);
p.exponent = exp10;
if (p.scalesize > 0)
{
for (i = 0; p.scale[i] == 0 && p.frac[i] == 0; i++)
;
count_leading_zeros (cnt_h, p.scale[p.scalesize - 1]);
if (cnt_h == 0)
{
if (i > 0)
{
MPN_COPY_INCR (p.scale, p.scale + i, p.scalesize - i);
p.scalesize -= i;
MPN_COPY_INCR (p.frac, p.frac + i, p.fracsize - i);
p.fracsize -= i;
}
}
else
{
if (p.scale[i] != 0)
{
count_trailing_zeros (cnt_l, p.scale[i]);
if (p.frac[i] != 0)
{
int cnt_l2;
count_trailing_zeros (cnt_l2, p.frac[i]);
if (cnt_l2 < cnt_l)
cnt_l = cnt_l2;
}
}
else
count_trailing_zeros (cnt_l, p.frac[i]);
if (i == 0 && BITS_PER_MP_LIMB - cnt_h > cnt_l)
{
(void) __mpn_lshift (p.scale, p.scale, p.scalesize, cnt_h);
cy = __mpn_lshift (p.frac, p.frac, p.fracsize, cnt_h);
if (cy != 0)
p.frac[p.fracsize++] = cy;
}
else if (BITS_PER_MP_LIMB - cnt_h <= cnt_l)
{
(void) __mpn_rshift (p.scale, p.scale + i, p.scalesize - i,
BITS_PER_MP_LIMB - cnt_h);
p.scalesize -= i + 1;
(void) __mpn_rshift (p.frac, p.frac + i, p.fracsize - i,
BITS_PER_MP_LIMB - cnt_h);
p.fracsize -= p.frac[p.fracsize - i - 1] == 0 ? i + 1 : i;
}
else
{
(void) __mpn_rshift (p.scale, p.scale + (i - 1),
p.scalesize - (i - 1),
BITS_PER_MP_LIMB - cnt_h);
p.scalesize -= i;
(void) __mpn_rshift (p.frac, p.frac + (i - 1),
p.fracsize - (i - 1),
BITS_PER_MP_LIMB - cnt_h);
p.fracsize -=
p.frac[p.fracsize - (i - 1) - 1] == 0 ? i : i - 1;
}
}
}
}
else if (p.exponent < 0)
{
int exp10 = 0;
int explog = LDBL_MAX_10_EXP_LOG;
const struct mp_power *powers = &_fpioconst_pow10[explog + 1];
cy = __mpn_lshift (p.frac, fp_input, p.fracsize, to_shift);
p.frac[p.fracsize++] = cy;
assert (cy == 1 || (p.frac[p.fracsize - 2] == 0 && p.frac[0] == 0));
p.expsign = 1;
p.exponent = -p.exponent;
assert (powers != &_fpioconst_pow10[0]);
do
{
--powers;
if (p.exponent >= powers->m_expo)
{
int i, incr, cnt_h, cnt_l;
mp_limb_t topval[2];
if (p.fracsize < powers->arraysize - _FPIO_CONST_OFFSET)
cy = __mpn_mul (p.tmp, &__tens[powers->arrayoff
+ _FPIO_CONST_OFFSET],
powers->arraysize - _FPIO_CONST_OFFSET,
p.frac, p.fracsize);
else
cy = __mpn_mul (p.tmp, p.frac, p.fracsize,
&__tens[powers->arrayoff + _FPIO_CONST_OFFSET],
powers->arraysize - _FPIO_CONST_OFFSET);
p.tmpsize = p.fracsize + powers->arraysize - _FPIO_CONST_OFFSET;
if (cy == 0)
--p.tmpsize;
count_leading_zeros (cnt_h, p.tmp[p.tmpsize - 1]);
incr = (p.tmpsize - p.fracsize) * BITS_PER_MP_LIMB
+ BITS_PER_MP_LIMB - 1 - cnt_h;
assert (incr <= powers->p_expo);
if (incr == p.exponent + 3)
{
if (cnt_h <= BITS_PER_MP_LIMB - 4)
{
topval[0] = 0;
topval[1]
= ((mp_limb_t) 10) << (BITS_PER_MP_LIMB - 4 - cnt_h);
}
else
{
topval[0] = ((mp_limb_t) 10) << (BITS_PER_MP_LIMB - 4);
topval[1] = 0;
(void) __mpn_lshift (topval, topval, 2,
BITS_PER_MP_LIMB - cnt_h);
}
}
if (incr < p.exponent + 3
|| (incr == p.exponent + 3 &&
(p.tmp[p.tmpsize - 1] < topval[1]
|| (p.tmp[p.tmpsize - 1] == topval[1]
&& p.tmp[p.tmpsize - 2] < topval[0]))))
{
p.exponent -= incr;
exp10 |= 1 << explog;
if (p.exponent < 0)
cnt_h += -p.exponent;
for (i = 0; p.tmp[i] == 0; ++i);
if (cnt_h == BITS_PER_MP_LIMB - 1)
{
MPN_COPY (p.frac, p.tmp + i, p.tmpsize - i);
p.fracsize = p.tmpsize - i;
}
else
{
count_trailing_zeros (cnt_l, p.tmp[i]);
if (i == 0 && BITS_PER_MP_LIMB - 1 - cnt_h > cnt_l)
{
cy = __mpn_lshift (p.frac, p.tmp, p.tmpsize,
cnt_h + 1);
p.fracsize = p.tmpsize + 1;
p.frac[p.fracsize - 1] = cy;
}
else if (BITS_PER_MP_LIMB - 1 - cnt_h <= cnt_l)
{
(void) __mpn_rshift (p.frac, p.tmp + i, p.tmpsize - i,
BITS_PER_MP_LIMB - 1 - cnt_h);
p.fracsize = p.tmpsize - i;
}
else
{
(void) __mpn_rshift (p.frac, p.tmp + (i - 1),
p.tmpsize - (i - 1),
BITS_PER_MP_LIMB - 1 - cnt_h);
p.fracsize = p.tmpsize - (i - 1);
}
}
}
}
--explog;
}
while (powers != &_fpioconst_pow10[1] && p.exponent > 0);
if (p.exponent > 0)
{
int cnt_l;
cy = __mpn_mul_1 (p.tmp, p.frac, p.fracsize, 10);
p.tmpsize = p.fracsize;
assert (cy == 0 || p.tmp[p.tmpsize - 1] < 20);
count_trailing_zeros (cnt_l, p.tmp[0]);
if (cnt_l < MIN (4, p.exponent))
{
cy = __mpn_lshift (p.frac, p.tmp, p.tmpsize,
BITS_PER_MP_LIMB - MIN (4, p.exponent));
if (cy != 0)
p.frac[p.tmpsize++] = cy;
}
else
(void) __mpn_rshift (p.frac, p.tmp, p.tmpsize, MIN (4, p.exponent));
p.fracsize = p.tmpsize;
exp10 |= 1;
assert (p.frac[p.fracsize - 1] < 10);
}
p.exponent = exp10;
}
else
{
assert (0 <= p.exponent && p.exponent < 3 &&
p.exponent + to_shift < BITS_PER_MP_LIMB);
cy = __mpn_lshift (p.frac, fp_input, p.fracsize, (p.exponent + to_shift));
p.frac[p.fracsize++] = cy;
p.exponent = 0;
}
{
int width = info->width;
wchar_t *wstartp, *wcp;
size_t chars_needed;
int expscale;
int intdig_max, intdig_no = 0;
int fracdig_min;
int fracdig_max;
int dig_max;
int significant;
int ngroups = 0;
char spec = _tolower (info->spec);
size_t wbuffer_to_alloc;
if (spec == 'e')
{
p.type = info->spec;
intdig_max = 1;
fracdig_min = fracdig_max = info->prec < 0 ? 6 : info->prec;
chars_needed = 1 + 1 + (size_t) fracdig_max + 1 + 1 + 4;
dig_max = INT_MAX;
significant = 1;
}
else if (spec == 'f')
{
p.type = 'f';
fracdig_min = fracdig_max = info->prec < 0 ? 6 : info->prec;
dig_max = INT_MAX;
significant = 1;
if (p.expsign == 0)
{
intdig_max = p.exponent + 1;
chars_needed = (size_t) p.exponent + 1 + 1 + (size_t) fracdig_max;
}
else
{
intdig_max = 1;
chars_needed = 1 + 1 + (size_t) fracdig_max;
}
}
else
{
dig_max = info->prec < 0 ? 6 : (info->prec == 0 ? 1 : info->prec);
if ((p.expsign == 0 && p.exponent >= dig_max)
|| (p.expsign != 0 && p.exponent > 4))
{
if ('g' - 'G' == 'e' - 'E')
p.type = 'E' + (info->spec - 'G');
else
p.type = isupper (info->spec) ? 'E' : 'e';
fracdig_max = dig_max - 1;
intdig_max = 1;
chars_needed = 1 + 1 + (size_t) fracdig_max + 1 + 1 + 4;
}
else
{
p.type = 'f';
intdig_max = p.expsign == 0 ? p.exponent + 1 : 0;
fracdig_max = dig_max - intdig_max;
chars_needed = (size_t) dig_max + 1 + 4;
}
fracdig_min = info->alt ? fracdig_max : 0;
significant = 0;
}
if (grouping)
{
ngroups = __guess_grouping (intdig_max, grouping);
chars_needed += ngroups + 1;
}
if (__builtin_expect (chars_needed >= (size_t) -1 / sizeof (wchar_t) - 2
|| chars_needed < fracdig_max, 0))
{
__set_errno (ERANGE);
return -1;
}
wbuffer_to_alloc = (2 + chars_needed) * sizeof (wchar_t);
buffer_malloced = ! __libc_use_alloca (wbuffer_to_alloc);
if (__builtin_expect (buffer_malloced, 0))
{
wbuffer = (wchar_t *) malloc (wbuffer_to_alloc);
if (wbuffer == NULL)
return -1;
}
else
wbuffer = (wchar_t *) alloca (wbuffer_to_alloc);
wcp = wstartp = wbuffer + 2;
if (p.expsign == 0 || p.type != 'f')
{
assert (p.expsign == 0 || intdig_max == 1);
while (intdig_no < intdig_max)
{
++intdig_no;
*wcp++ = hack_digit (&p);
}
significant = 1;
if (info->alt
|| fracdig_min > 0
|| (fracdig_max > 0 && (p.fracsize > 1 || p.frac[0] != 0)))
*wcp++ = decimalwc;
}
else
{
*wcp++ = L'0';
--p.exponent;
*wcp++ = decimalwc;
}
{
wchar_t last_digit, next_digit;
bool more_bits;
size_t lcnt;
int rounding_mode;
wchar_t *wtp;
int fracdig_no = 0;
int added_zeros = 0;
while (fracdig_no < fracdig_min + added_zeros
|| (fracdig_no < fracdig_max && (p.fracsize > 1 || p.frac[0] != 0)))
{
++fracdig_no;
*wcp = hack_digit (&p);
if (*wcp++ != L'0')
significant = 1;
else if (significant == 0)
{
++fracdig_max;
if (fracdig_min > 0)
++added_zeros;
}
}
last_digit = wcp[-1] != decimalwc ? wcp[-1] : wcp[-2];
next_digit = hack_digit (&p);
more_bits;
if (next_digit != L'0' && next_digit != L'5')
more_bits = true;
else if (p.fracsize == 1 && p.frac[0] == 0)
more_bits = false;
else if (p.scalesize == 0)
{
lcnt = p.fracsize;
while (lcnt >= 1 && p.frac[lcnt - 1] == 0)
--lcnt;
more_bits = lcnt > 0;
}
else
more_bits = true;
rounding_mode = get_rounding_mode ();
if (round_away (is_neg, (last_digit - L'0') & 1, next_digit >= L'5',
more_bits, rounding_mode))
{
wtp = wcp;
if (fracdig_no > 0)
{
int removed = 0;
while (*--wtp != decimalwc && *wtp == L'9')
{
*wtp = L'0';
++removed;
}
if (removed == fracdig_min && added_zeros > 0)
--added_zeros;
if (*wtp != decimalwc)
(*wtp)++;
else if (__builtin_expect (spec == 'g' && p.type == 'f' && info->alt
&& wtp == wstartp + 1
&& wstartp[0] == L'0',
0))
--added_zeros;
}
if (fracdig_no == 0 || *wtp == decimalwc)
{
if (*(wtp - 1) == decimalwc)
--wtp;
while (--wtp >= wstartp && *wtp == L'9')
*wtp = L'0';
if (wtp >= wstartp)
(*wtp)++;
else
{
if (p.type != 'f')
{
*wstartp = '1';
p.exponent += p.expsign == 0 ? 1 : -1;
if (p.exponent == 0)
p.expsign = 0;
}
else if (intdig_no == dig_max)
{
*--wstartp = decimalwc;
*--wstartp = L'1';
if (info->alt || fracdig_no > 0)
{
wstartp[intdig_no + 2] = L'0';
++fracdig_no;
}
fracdig_no += intdig_no;
intdig_no = 1;
fracdig_max = intdig_max - intdig_no;
++p.exponent;
p.type = isupper (info->spec) ? 'E' : 'e';
}
else
{
*--wstartp = L'1';
++intdig_no;
}
if (intdig_no + fracdig_no > dig_max)
{
wcp -= intdig_no + fracdig_no - dig_max;
fracdig_no -= intdig_no + fracdig_no - dig_max;
}
}
}
}
while (fracdig_no > fracdig_min + added_zeros && *(wcp - 1) == L'0')
{
--wcp;
--fracdig_no;
}
if (fracdig_no == 0 && !info->alt && *(wcp - 1) == decimalwc)
--wcp;
if (grouping)
{
if (intdig_no != intdig_max)
ngroups = __guess_grouping (intdig_no, grouping);
wcp = group_number (wstartp, wcp, intdig_no, grouping, thousands_sepwc,
ngroups);
}
if (p.type != 'f')
{
if (__glibc_unlikely (p.expsign != 0 && p.exponent == 4 && spec == 'g'))
{
assert (wcp >= wstartp + 1);
assert (wstartp[0] == L'1');
__wmemcpy (wstartp, L"0.0001", 6);
wstartp[1] = decimalwc;
if (wcp >= wstartp + 2)
{
wmemset (wstartp + 6, L'0', wcp - (wstartp + 2));
wcp += 4;
}
else
wcp += 5;
}
else
{
*wcp++ = (wchar_t) p.type;
*wcp++ = p.expsign ? L'-' : L'+';
expscale = 10;
while (expscale <= p.exponent)
expscale *= 10;
if (p.exponent < 10)
*wcp++ = L'0';
else
do
{
expscale /= 10;
*wcp++ = L'0' + (p.exponent / expscale);
p.exponent %= expscale;
}
while (expscale > 10);
*wcp++ = L'0' + p.exponent;
}
}
if (is_neg || info->showsign || info->space)
--width;
width -= wcp - wstartp;
if (!info->left && info->pad != '0' && width > 0)
PADN (info->pad, width);
if (is_neg)
outchar ('-');
else if (info->showsign)
outchar ('+');
else if (info->space)
outchar (' ');
if (!info->left && info->pad == '0' && width > 0)
PADN ('0', width);
{
char *buffer = NULL;
char *buffer_end = NULL;
char *cp = NULL;
char *tmpptr;
if (! wide)
{
size_t decimal_len;
size_t thousands_sep_len;
size_t nbuffer;
wchar_t *copywc;
size_t factor = (info->i18n
? _NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MB_CUR_MAX)
: 1);
decimal_len = strlen (decimal);
if (thousands_sep == NULL)
thousands_sep_len = 0;
else
thousands_sep_len = strlen (thousands_sep);
nbuffer = (2 + chars_needed * factor + decimal_len
+ ngroups * thousands_sep_len);
if (__glibc_unlikely (buffer_malloced))
{
buffer = (char *) malloc (nbuffer);
if (buffer == NULL)
{
free (wbuffer);
return -1;
}
}
else
buffer = (char *) alloca (nbuffer);
buffer_end = buffer + nbuffer;
for (cp = buffer, copywc = wstartp; copywc < wcp; ++copywc)
if (*copywc == decimalwc)
cp = (char *) __mempcpy (cp, decimal, decimal_len);
else if (*copywc == thousands_sepwc)
cp = (char *) __mempcpy (cp, thousands_sep, thousands_sep_len);
else
*cp++ = (char) *copywc;
}
tmpptr = buffer;
if (__glibc_unlikely (info->i18n))
{
#ifdef COMPILE_WPRINTF
wstartp = _i18n_number_rewrite (wstartp, wcp,
wbuffer + wbuffer_to_alloc);
wcp = wbuffer + wbuffer_to_alloc;
assert ((uintptr_t) wbuffer <= (uintptr_t) wstartp);
assert ((uintptr_t) wstartp
< (uintptr_t) wbuffer + wbuffer_to_alloc);
#else
tmpptr = _i18n_number_rewrite (tmpptr, cp, buffer_end);
cp = buffer_end;
assert ((uintptr_t) buffer <= (uintptr_t) tmpptr);
assert ((uintptr_t) tmpptr < (uintptr_t) buffer_end);
#endif
}
PRINT (tmpptr, wstartp, wide ? wcp - wstartp : cp - tmpptr);
if (__glibc_unlikely (buffer_malloced))
{
free (buffer);
free (wbuffer);
}
}
if (info->left && width > 0)
PADN (info->pad, width);
}
}
return done;
}
unsigned int
__guess_grouping (unsigned int intdig_max, const char *grouping)
{
unsigned int groups;
if (*grouping == CHAR_MAX || *grouping <= 0)
return 0;
groups = 0;
while (intdig_max > (unsigned int) *grouping)
{
++groups;
intdig_max -= *grouping++;
if (*grouping == CHAR_MAX
#if CHAR_MIN < 0
|| *grouping < 0
#endif
)
break;
else if (*grouping == 0)
{
groups += (intdig_max - 1) / grouping[-1];
break;
}
}
return groups;
}
static wchar_t *
internal_function
group_number (wchar_t *buf, wchar_t *bufend, unsigned int intdig_no,
const char *grouping, wchar_t thousands_sep, int ngroups)
{
wchar_t *p;
if (ngroups == 0)
return bufend;
__wmemmove (buf + intdig_no + ngroups, buf + intdig_no,
bufend - (buf + intdig_no));
p = buf + intdig_no + ngroups - 1;
do
{
unsigned int len = *grouping++;
do
*p-- = buf[--intdig_no];
while (--len > 0);
*p-- = thousands_sep;
if (*grouping == CHAR_MAX
#if CHAR_MIN < 0
|| *grouping < 0
#endif
)
break;
else if (*grouping == 0)
--grouping;
} while (intdig_no > (unsigned int) *grouping);
do
*p-- = buf[--intdig_no];
while (p > buf);
return bufend + ngroups;
}