diff options
Diffstat (limited to 'gl/vasnprintf.c')
-rw-r--r-- | gl/vasnprintf.c | 361 |
1 files changed, 272 insertions, 89 deletions
diff --git a/gl/vasnprintf.c b/gl/vasnprintf.c index d5b40286..93aef6f2 100644 --- a/gl/vasnprintf.c +++ b/gl/vasnprintf.c @@ -95,7 +95,7 @@ #if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL # include <math.h> -# include "isnand.h" +# include "isnand-nolibm.h" #endif #if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE) && !defined IN_LIBINTL @@ -106,7 +106,7 @@ #if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL # include <math.h> -# include "isnand.h" +# include "isnand-nolibm.h" # include "printf-frexp.h" #endif @@ -117,11 +117,6 @@ # include "fpucw.h" #endif -/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */ -#ifndef EOVERFLOW -# define EOVERFLOW E2BIG -#endif - #if HAVE_WCHAR_T # if HAVE_WCSLEN # define local_wcslen wcslen @@ -182,10 +177,12 @@ local_wcslen (const wchar_t *s) # endif #else /* TCHAR_T is char. */ -# /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. + /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. But don't use it on BeOS, since BeOS snprintf produces no output if the - size argument is >= 0x3000000. */ -# if (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) && !defined __BEOS__ + size argument is >= 0x3000000. + Also don't use it on Linux libc5, since there snprintf with size = 1 + writes any output without bounds, like sprintf. */ +# if (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) && !defined __BEOS__ && !(__GNU_LIBRARY__ == 1) # define USE_SNPRINTF 1 # else # define USE_SNPRINTF 0 @@ -203,7 +200,22 @@ local_wcslen (const wchar_t *s) /* Here we need to call the native sprintf, not rpl_sprintf. */ #undef sprintf -#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL +/* GCC >= 4.0 with -Wall emits unjustified "... may be used uninitialized" + warnings in this file. Use -Dlint to suppress them. */ +#ifdef lint +# define IF_LINT(Code) Code +#else +# define IF_LINT(Code) /* empty */ +#endif + +/* Avoid some warnings from "gcc -Wshadow". + This file doesn't use the exp() and remainder() functions. */ +#undef exp +#define exp expo +#undef remainder +#define remainder rem + +#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL /* Determine the decimal-point character according to the current locale. */ # ifndef decimal_point_char_defined # define decimal_point_char_defined 1 @@ -243,11 +255,11 @@ is_infinite_or_zero (double x) #if NEED_PRINTF_INFINITE_LONG_DOUBLE && !NEED_PRINTF_LONG_DOUBLE && !defined IN_LIBINTL -/* Equivalent to !isfinite(x), but does not require libm. */ +/* Equivalent to !isfinite(x) || x == 0, but does not require libm. */ static int -is_infinitel (long double x) +is_infinite_or_zerol (long double x) { - return isnanl (x) || (x + x == x && x != 0.0L); + return isnanl (x) || x + x == x; } #endif @@ -1201,7 +1213,7 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) static char * scale10_round_decimal_long_double (long double x, int n) { - int e; + int e IF_LINT(= 0); mpn_t m; void *memory = decode_long_double (x, &e, &m); return scale10_round_decimal_decoded (e, m, memory, n); @@ -1219,7 +1231,7 @@ scale10_round_decimal_long_double (long double x, int n) static char * scale10_round_decimal_double (double x, int n) { - int e; + int e IF_LINT(= 0); mpn_t m; void *memory = decode_double (x, &e, &m); return scale10_round_decimal_decoded (e, m, memory, n); @@ -1306,9 +1318,9 @@ floorlog10l (long double x) } /* Now 0.95 <= z <= 1.01. */ z = 1 - z; - /* log(1-z) = - z - z^2/2 - z^3/3 - z^4/4 - ... + /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...) Four terms are enough to get an approximation with error < 10^-7. */ - l -= z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); + l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); /* Finally multiply with log(2)/log(10), yields an approximation for log10(x). */ l *= 0.30102999566398119523; @@ -1397,9 +1409,9 @@ floorlog10 (double x) } /* Now 0.95 <= z <= 1.01. */ z = 1 - z; - /* log(1-z) = - z - z^2/2 - z^3/3 - z^4/4 - ... + /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...) Four terms are enough to get an approximation with error < 10^-7. */ - l -= z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); + l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); /* Finally multiply with log(2)/log(10), yields an approximation for log10(x). */ l *= 0.30102999566398119523; @@ -1409,6 +1421,20 @@ floorlog10 (double x) # endif +/* Tests whether a string of digits consists of exactly PRECISION zeroes and + a single '1' digit. */ +static int +is_borderline (const char *digits, size_t precision) +{ + for (; precision > 0; precision--, digits++) + if (*digits != '0') + return 0; + if (*digits != '1') + return 0; + digits++; + return *digits == '\0'; +} + #endif DCHAR_T * @@ -2552,8 +2578,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # elif NEED_PRINTF_INFINITE_LONG_DOUBLE || (a.arg[dp->arg_index].type == TYPE_LONGDOUBLE /* Some systems produce wrong output for Inf, - -Inf, and NaN. */ - && is_infinitel (a.arg[dp->arg_index].a.a_longdouble)) + -Inf, and NaN. Some systems in this category + (IRIX 5.3) also do so for -0.0. Therefore we + treat this case here as well. */ + && is_infinite_or_zerol (a.arg[dp->arg_index].a.a_longdouble)) # endif )) { @@ -2635,9 +2663,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* POSIX specifies the default precision to be 6 for %f, %F, %e, %E, but not for %g, %G. Implementations appear to use - the same default precision also for %g, %G. */ + the same default precision also for %g, %G. But for %a, %A, + the default precision is 0. */ if (!has_precision) - precision = 6; + if (!(dp->conversion == 'a' || dp->conversion == 'A')) + precision = 6; /* Allocate a temporary buffer of sufficient size. */ # if NEED_PRINTF_DOUBLE && NEED_PRINTF_LONG_DOUBLE @@ -2858,8 +2888,32 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, exponent += 1; adjusted = 1; } - /* Here ndigits = precision+1. */ + if (is_borderline (digits, precision)) + { + /* Maybe the exponent guess was too high + and a smaller exponent can be reached + by turning a 10...0 into 9...9x. */ + char *digits2 = + scale10_round_decimal_long_double (arg, + (int)precision - exponent + 1); + if (digits2 == NULL) + { + free (digits); + END_LONG_DOUBLE_ROUNDING (); + goto out_of_memory; + } + if (strlen (digits2) == precision + 1) + { + free (digits); + digits = digits2; + exponent -= 1; + } + else + free (digits2); + } + /* Here ndigits = precision+1. */ + *p++ = digits[--ndigits]; if ((flags & FLAG_ALT) || precision > 0) { @@ -2971,6 +3025,30 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, adjusted = 1; } /* Here ndigits = precision. */ + if (is_borderline (digits, precision - 1)) + { + /* Maybe the exponent guess was too high + and a smaller exponent can be reached + by turning a 10...0 into 9...9x. */ + char *digits2 = + scale10_round_decimal_long_double (arg, + (int)(precision - 1) - exponent + 1); + if (digits2 == NULL) + { + free (digits); + END_LONG_DOUBLE_ROUNDING (); + goto out_of_memory; + } + if (strlen (digits2) == precision) + { + free (digits); + digits = digits2; + exponent -= 1; + } + else + free (digits2); + } + /* Here ndigits = precision. */ /* Determine the number of trailing zeroes that have to be dropped. */ @@ -3065,7 +3143,65 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, abort (); # else /* arg is finite. */ - abort (); + if (!(arg == 0.0L)) + abort (); + + pad_ptr = p; + + if (dp->conversion == 'f' || dp->conversion == 'F') + { + *p++ = '0'; + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + for (; precision > 0; precision--) + *p++ = '0'; + } + } + else if (dp->conversion == 'e' || dp->conversion == 'E') + { + *p++ = '0'; + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + for (; precision > 0; precision--) + *p++ = '0'; + } + *p++ = dp->conversion; /* 'e' or 'E' */ + *p++ = '+'; + *p++ = '0'; + *p++ = '0'; + } + else if (dp->conversion == 'g' || dp->conversion == 'G') + { + *p++ = '0'; + if (flags & FLAG_ALT) + { + size_t ndigits = + (precision > 0 ? precision - 1 : 0); + *p++ = decimal_point_char (); + for (; ndigits > 0; --ndigits) + *p++ = '0'; + } + } + else if (dp->conversion == 'a' || dp->conversion == 'A') + { + *p++ = '0'; + *p++ = dp->conversion - 'A' + 'X'; + pad_ptr = p; + *p++ = '0'; + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + for (; precision > 0; precision--) + *p++ = '0'; + } + *p++ = dp->conversion - 'A' + 'P'; + *p++ = '+'; + *p++ = '0'; + } + else + abort (); # endif } @@ -3211,8 +3347,31 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, exponent += 1; adjusted = 1; } - /* Here ndigits = precision+1. */ + if (is_borderline (digits, precision)) + { + /* Maybe the exponent guess was too high + and a smaller exponent can be reached + by turning a 10...0 into 9...9x. */ + char *digits2 = + scale10_round_decimal_double (arg, + (int)precision - exponent + 1); + if (digits2 == NULL) + { + free (digits); + goto out_of_memory; + } + if (strlen (digits2) == precision + 1) + { + free (digits); + digits = digits2; + exponent -= 1; + } + else + free (digits2); + } + /* Here ndigits = precision+1. */ + *p++ = digits[--ndigits]; if ((flags & FLAG_ALT) || precision > 0) { @@ -3337,6 +3496,29 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, adjusted = 1; } /* Here ndigits = precision. */ + if (is_borderline (digits, precision - 1)) + { + /* Maybe the exponent guess was too high + and a smaller exponent can be reached + by turning a 10...0 into 9...9x. */ + char *digits2 = + scale10_round_decimal_double (arg, + (int)(precision - 1) - exponent + 1); + if (digits2 == NULL) + { + free (digits); + goto out_of_memory; + } + if (strlen (digits2) == precision) + { + free (digits); + digits = digits2; + exponent -= 1; + } + else + free (digits2); + } + /* Here ndigits = precision. */ /* Determine the number of trailing zeroes that have to be dropped. */ @@ -3588,7 +3770,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #endif TCHAR_T *fbp; unsigned int prefix_count; - int prefixes[2]; + int prefixes[2] IF_LINT (= { 0 }); #if !USE_SNPRINTF size_t tmp_length; TCHAR_T tmpbuf[700]; @@ -3661,6 +3843,44 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } #endif + /* Decide whether to handle the precision ourselves. */ +#if NEED_PRINTF_UNBOUNDED_PRECISION + switch (dp->conversion) + { + case 'd': case 'i': case 'u': + case 'o': + case 'x': case 'X': case 'p': + prec_ourselves = has_precision && (precision > 0); + break; + default: + prec_ourselves = 0; + break; + } +#endif + + /* Decide whether to perform the padding ourselves. */ +#if !NEED_PRINTF_FLAG_LEFTADJUST && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION) + switch (dp->conversion) + { +# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO + /* If we need conversion from TCHAR_T[] to DCHAR_T[], we need + to perform the padding after this conversion. Functions + with unistdio extensions perform the padding based on + character count rather than element count. */ + case 'c': case 's': +# endif +# if NEED_PRINTF_FLAG_ZERO + case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': + case 'a': case 'A': +# endif + pad_ourselves = 1; + break; + default: + pad_ourselves = prec_ourselves; + break; + } +#endif + #if !USE_SNPRINTF /* Allocate a temporary buffer of sufficient size for calling sprintf. */ @@ -3837,18 +4057,22 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, abort (); } + if (!pad_ourselves) + { # if ENABLE_UNISTDIO - /* Padding considers the number of characters, therefore the - number of elements after padding may be - > max (tmp_length, width) - but is certainly - <= tmp_length + width. */ - tmp_length = xsum (tmp_length, width); + /* Padding considers the number of characters, therefore + the number of elements after padding may be + > max (tmp_length, width) + but is certainly + <= tmp_length + width. */ + tmp_length = xsum (tmp_length, width); # else - /* Padding considers the number of elements, says POSIX. */ - if (tmp_length < width) - tmp_length = width; + /* Padding considers the number of elements, + says POSIX. */ + if (tmp_length < width) + tmp_length = width; # endif + } tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ } @@ -3869,44 +4093,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } #endif - /* Decide whether to handle the precision ourselves. */ -#if NEED_PRINTF_UNBOUNDED_PRECISION - switch (dp->conversion) - { - case 'd': case 'i': case 'u': - case 'o': - case 'x': case 'X': case 'p': - prec_ourselves = has_precision && (precision > 0); - break; - default: - prec_ourselves = 0; - break; - } -#endif - - /* Decide whether to perform the padding ourselves. */ -#if !NEED_PRINTF_FLAG_LEFTADJUST && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION) - switch (dp->conversion) - { -# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO - /* If we need conversion from TCHAR_T[] to DCHAR_T[], we need - to perform the padding after this conversion. Functions - with unistdio extensions perform the padding based on - character count rather than element count. */ - case 'c': case 's': -# endif -# if NEED_PRINTF_FLAG_ZERO - case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': - case 'a': case 'A': -# endif - pad_ourselves = 1; - break; - default: - pad_ourselves = prec_ourselves; - break; - } -#endif - /* Construct the format string for calling snprintf or sprintf. */ fbp = buf; @@ -4052,7 +4238,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, abort (); prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int; } - if (dp->precision_arg_index != ARG_NONE) + if (!prec_ourselves && dp->precision_arg_index != ARG_NONE) { if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); @@ -4403,14 +4589,14 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } #endif -#if !DCHAR_IS_TCHAR -# if !USE_SNPRINTF +#if !USE_SNPRINTF if (count >= tmp_length) /* tmp_length was incorrectly calculated - fix the code above! */ abort (); -# endif +#endif +#if !DCHAR_IS_TCHAR /* Convert from TCHAR_T[] to DCHAR_T[]. */ if (dp->conversion == 'c' || dp->conversion == 's') { @@ -4528,7 +4714,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (w < width) { size_t pad = width - w; -# if USE_SNPRINTF + /* Make room for the result. */ if (xsum (count, pad) > allocated - length) { @@ -4538,12 +4724,16 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, xmax (xsum3 (length, count, pad), xtimes (allocated, 2)); +# if USE_SNPRINTF length += count; ENSURE_ALLOCATION (n); length -= count; +# else + ENSURE_ALLOCATION (n); +# endif } /* Here count + pad <= allocated - length. */ -# endif + { # if !DCHAR_IS_TCHAR || USE_SNPRINTF DCHAR_T * const rp = result + length; @@ -4553,7 +4743,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, DCHAR_T *p = rp + count; DCHAR_T *end = p + pad; DCHAR_T *pad_ptr; -# if !DCHAR_IS_TCHAR +# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO if (dp->conversion == 'c' || dp->conversion == 's') /* No zero-padding for string directives. */ @@ -4604,13 +4794,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } #endif -#if DCHAR_IS_TCHAR && !USE_SNPRINTF - if (count >= tmp_length) - /* tmp_length was incorrectly calculated - fix the - code above! */ - abort (); -#endif - /* Here still count <= allocated - length. */ #if !DCHAR_IS_TCHAR || USE_SNPRINTF |