X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fvasnprintf.c;h=e11f1e998306151821a900287727e9832fcd55ab;hb=5d0b385594bc914e6233988bfb6bc1b92a2184b5;hp=8af1063164ae24a22ca312704fb5b569cfc2bb69;hpb=b4548f0e8f674d7c914b622c5ace7e2686ab0f87;p=gnulib.git diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 8af106316..e11f1e998 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -34,12 +34,16 @@ # include "vasnprintf.h" #endif +#include /* localeconv() */ #include /* snprintf(), sprintf() */ #include /* abort(), malloc(), realloc(), free() */ #include /* memcpy(), strlen() */ #include /* errno */ #include /* CHAR_BIT */ #include /* DBL_MAX_EXP, LDBL_MAX_EXP */ +#if HAVE_NL_LANGINFO +# include +#endif #if WIDE_CHAR_VERSION # include "wprintf-parse.h" #else @@ -50,12 +54,17 @@ #include "xsize.h" #if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL +# include # include "isnan.h" -# include "isnanl.h" -# if HAVE_LONG_DOUBLE -# include "printf-frexp.h" -# include "printf-frexpl.h" -# endif +# include "printf-frexp.h" +# include "isnanl-nolibm.h" +# include "printf-frexpl.h" +# 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 @@ -109,6 +118,37 @@ local_wcslen (const wchar_t *s) # else /* Unix. */ # define SNPRINTF snprintf + /* Here we need to call the native snprintf, not rpl_snprintf. */ +# undef snprintf +# endif +#endif +/* Here we need to call the native sprintf, not rpl_sprintf. */ +#undef sprintf + +#if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL +/* Determine the decimal-point character according to the current locale. */ +# ifndef decimal_point_char_defined +# define decimal_point_char_defined 1 +static char +decimal_point_char () +{ + const char *point; + /* Determine it in a multithread-safe way. We know nl_langinfo is + multithread-safe on glibc systems, but is not required to be multithread- + safe by POSIX. sprintf(), however, is multithread-safe. localeconv() + is rarely multithread-safe. */ +# if HAVE_NL_LANGINFO && __GLIBC__ + point = nl_langinfo (RADIXCHAR); +# elif 1 + char pointbuf[5]; + sprintf (pointbuf, "%#.0f", 1.0); + point = &pointbuf[1]; +# else + point = localeconv () -> decimal_point; +# endif + /* The decimal point is always a single byte: either '.' or ','. */ + return (point[0] != '\0' ? point[0] : '.'); +} # endif #endif @@ -344,7 +384,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar } /* Allocate a temporary buffer of sufficient size. */ -# if HAVE_LONG_DOUBLE if (type == TYPE_LONGDOUBLE) tmp_length = (unsigned int) ((LDBL_DIG + 1) @@ -352,7 +391,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar ) + 1; /* turn floor into ceil */ else -# endif tmp_length = (unsigned int) ((DBL_DIG + 1) * 0.831 /* decimal -> hexadecimal */ @@ -385,7 +423,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar pad_ptr = NULL; p = tmp; -# if HAVE_LONG_DOUBLE if (type == TYPE_LONGDOUBLE) { long double arg = a.arg[dp->arg_index].a.a_longdouble; @@ -404,23 +441,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar else { int sign = 0; + DECL_LONG_DOUBLE_ROUNDING + + BEGIN_LONG_DOUBLE_ROUNDING (); - if (arg < 0.0L) + if (signbit (arg)) /* arg < 0.0L or negative zero */ { sign = -1; arg = -arg; } - else if (arg == 0.0L) - { - /* Distinguish 0.0L and -0.0L. */ - static long double plus_zero = 0.0L; - long double arg_mem = arg; - if (memcmp (&plus_zero, &arg_mem, sizeof (long double)) != 0) - { - sign = -1; - arg = -arg; - } - } if (sign < 0) *p++ = '-'; @@ -429,7 +458,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar else if (flags & FLAG_SPACE) *p++ = ' '; - if (x > 0.0L && x + x == x) + if (arg > 0.0L && arg + arg == arg) { if (dp->conversion == 'A') { @@ -445,7 +474,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar int exponent; long double mantissa; - if (x > 0.0L) + if (arg > 0.0L) mantissa = printf_frexpl (arg, &exponent); else { @@ -457,7 +486,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar && precision < (unsigned int) ((LDBL_DIG + 1) * 0.831) + 1) { /* Round the mantissa. */ - long double tail = arg; + long double tail = mantissa; size_t q; for (q = precision; ; q--) @@ -469,15 +498,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar if (digit & 1 ? tail >= 0.5L : tail > 0.5L) tail = 1 - tail; else - tail = 0; + tail = - tail; break; } tail *= 16.0L; } - if (tail > 0.0L) + if (tail != 0.0L) for (q = precision; q > 0; q--) tail *= 0.0625L; - arg += tail; + mantissa += tail; } *p++ = '0'; @@ -486,19 +515,20 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar { int digit; - digit = (int) arg; - arg -= digit; + digit = (int) mantissa; + mantissa -= digit; *p++ = '0' + digit; - if ((flags & FLAG_ALT) || arg > 0.0L) + if ((flags & FLAG_ALT) + || mantissa > 0.0L || precision > 0) { - *p++ = '.'; + *p++ = decimal_point_char (); /* This loop terminates because we assume that FLT_RADIX is a power of 2. */ - while (arg > 0.0L) + while (mantissa > 0.0L) { - arg *= 16.0L; - digit = (int) arg; - arg -= digit; + mantissa *= 16.0L; + digit = (int) mantissa; + mantissa -= digit; *p++ = digit + (digit < 10 ? '0' @@ -514,22 +544,23 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar } } *p++ = dp->conversion - 'A' + 'P'; -# if WIDE_CHAR_VERSION +# if WIDE_CHAR_VERSION { static const wchar_t decimal_format[] = - { '%', 'd', '\0' }; + { '%', '+', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } -# else - sprintf (p, "%d", exponent); -# endif +# else + sprintf (p, "%+d", exponent); +# endif while (*p != '\0') p++; } + + END_LONG_DOUBLE_ROUNDING (); } } else -# endif { double arg = a.arg[dp->arg_index].a.a_double; @@ -548,22 +579,11 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar { int sign = 0; - if (arg < 0.0) + if (signbit (arg)) /* arg < 0.0 or negative zero */ { sign = -1; arg = -arg; } - else if (arg == 0.0) - { - /* Distinguish 0.0 and -0.0. */ - static double plus_zero = 0.0; - double arg_mem = arg; - if (memcmp (&plus_zero, &arg_mem, sizeof (double)) != 0) - { - sign = -1; - arg = -arg; - } - } if (sign < 0) *p++ = '-'; @@ -572,7 +592,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar else if (flags & FLAG_SPACE) *p++ = ' '; - if (x > 0.0 && x + x == x) + if (arg > 0.0 && arg + arg == arg) { if (dp->conversion == 'A') { @@ -588,7 +608,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar int exponent; double mantissa; - if (x > 0.0) + if (arg > 0.0) mantissa = printf_frexp (arg, &exponent); else { @@ -600,7 +620,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar && precision < (unsigned int) ((DBL_DIG + 1) * 0.831) + 1) { /* Round the mantissa. */ - double tail = arg; + double tail = mantissa; size_t q; for (q = precision; ; q--) @@ -612,15 +632,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar if (digit & 1 ? tail >= 0.5 : tail > 0.5) tail = 1 - tail; else - tail = 0; + tail = - tail; break; } tail *= 16.0; } - if (tail > 0.0) + if (tail != 0.0) for (q = precision; q > 0; q--) tail *= 0.0625; - arg += tail; + mantissa += tail; } *p++ = '0'; @@ -629,19 +649,20 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar { int digit; - digit = (int) arg; - arg -= digit; + digit = (int) mantissa; + mantissa -= digit; *p++ = '0' + digit; - if ((flags & FLAG_ALT) || arg > 0.0) + if ((flags & FLAG_ALT) + || mantissa > 0.0 || precision > 0) { - *p++ = '.'; + *p++ = decimal_point_char (); /* This loop terminates because we assume that FLT_RADIX is a power of 2. */ - while (arg > 0.0) + while (mantissa > 0.0) { - arg *= 16.0; - digit = (int) arg; - arg -= digit; + mantissa *= 16.0; + digit = (int) mantissa; + mantissa -= digit; *p++ = digit + (digit < 10 ? '0' @@ -660,11 +681,11 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar # if WIDE_CHAR_VERSION { static const wchar_t decimal_format[] = - { '%', 'd', '\0' }; + { '%', '+', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } # else - sprintf (p, "%d", exponent); + sprintf (p, "%+d", exponent); # endif while (*p != '\0') p++; @@ -883,7 +904,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar break; case 'f': case 'F': -# if HAVE_LONG_DOUBLE if (type == TYPE_LONGDOUBLE) tmp_length = (unsigned int) (LDBL_MAX_EXP @@ -893,7 +913,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar + 1 /* turn floor into ceil */ + 10; /* sign, decimal point etc. */ else -# endif tmp_length = (unsigned int) (DBL_MAX_EXP * 0.30103 /* binary -> decimal */ @@ -911,7 +930,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar break; case 'a': case 'A': -# if HAVE_LONG_DOUBLE if (type == TYPE_LONGDOUBLE) tmp_length = (unsigned int) (LDBL_DIG @@ -919,7 +937,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar ) + 1; /* turn floor into ceil */ else -# endif tmp_length = (unsigned int) (DBL_DIG * 0.831 /* decimal -> hexadecimal */ @@ -1038,15 +1055,18 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar #endif *p++ = 'l'; break; -#if HAVE_LONG_DOUBLE case TYPE_LONGDOUBLE: *p++ = 'L'; break; -#endif default: break; } - *p = dp->conversion; +#if NEED_PRINTF_DIRECTIVE_F + if (dp->conversion == 'F') + *p = 'f'; + else +#endif + *p = dp->conversion; #if USE_SNPRINTF p[1] = '%'; p[2] = 'n'; @@ -1088,6 +1108,9 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar retcount = 0; #if USE_SNPRINTF + /* SNPRINTF can fail if maxlen > INT_MAX. */ + if (maxlen > INT_MAX) + goto overflow; # define SNPRINTF_BUF(arg) \ switch (prefix_count) \ { \ @@ -1196,14 +1219,12 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar SNPRINTF_BUF (arg); } break; -#if HAVE_LONG_DOUBLE case TYPE_LONGDOUBLE: { long double arg = a.arg[dp->arg_index].a.a_longdouble; SNPRINTF_BUF (arg); } break; -#endif case TYPE_CHAR: { int arg = a.arg[dp->arg_index].a.a_char; @@ -1332,6 +1353,18 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar free (tmp); #endif +#if NEED_PRINTF_DIRECTIVE_F + if (dp->conversion == 'F') + { + /* Convert the %f result to upper case for %F. */ + CHAR_T *rp = result + length; + size_t rc; + for (rc = count; rc > 0; rc--, rp++) + if (*rp >= 'a' && *rp <= 'z') + *rp = *rp - 'a' + 'A'; + } +#endif + length += count; break; } @@ -1363,6 +1396,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar not have this limitation. */ return result; + overflow: + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EOVERFLOW; + return NULL; + out_of_memory: if (!(result == resultbuf || result == NULL)) free (result);