diff options
Diffstat (limited to 'Build/source/libs/mpfr/mpfr-3.1.2/src/vasprintf.c')
-rw-r--r-- | Build/source/libs/mpfr/mpfr-3.1.2/src/vasprintf.c | 2086 |
1 files changed, 2086 insertions, 0 deletions
diff --git a/Build/source/libs/mpfr/mpfr-3.1.2/src/vasprintf.c b/Build/source/libs/mpfr/mpfr-3.1.2/src/vasprintf.c new file mode 100644 index 00000000000..31e001cb5b2 --- /dev/null +++ b/Build/source/libs/mpfr/mpfr-3.1.2/src/vasprintf.c @@ -0,0 +1,2086 @@ +/* mpfr_vasprintf -- main function for the printf functions family + plus helper macros & functions. + +Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. +Contributed by the AriC and Caramel projects, INRIA. + +This file is part of the GNU MPFR Library. + +The GNU MPFR Library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +The GNU MPFR Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see +http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* The mpfr_printf-like functions are defined only if <stdarg.h> exists */ +#ifdef HAVE_STDARG + +#include <stdarg.h> + +#ifndef HAVE_VA_COPY +# ifdef HAVE___VA_COPY +# define va_copy(dst,src) __va_copy(dst, src) +# else +/* autoconf manual advocates this fallback. + This is also the solution chosen by gmp */ +# define va_copy(dst,src) \ + do { memcpy(&(dst), &(src), sizeof(va_list)); } while (0) +# endif /* HAVE___VA_COPY */ +#endif /* HAVE_VA_COPY */ + +#ifdef HAVE_WCHAR_H +#include <wchar.h> +#endif + +#if defined (__cplusplus) +#include <cstddef> +#define __STDC_LIMIT_MACROS /* SIZE_MAX defined with <stdint.h> inclusion */ +#else +#include <stddef.h> /* for ptrdiff_t */ +#endif + +#define MPFR_NEED_LONGLONG_H +#include "mpfr-intmax.h" +#include "mpfr-impl.h" + +/* Define a length modifier corresponding to mpfr_prec_t. + We use literal string instead of literal character so as to permit future + extension to long long int ("ll"). */ +#if _MPFR_PREC_FORMAT == 1 +#define MPFR_PREC_FORMAT_TYPE "h" +#define MPFR_PREC_FORMAT_SIZE 1 +#elif _MPFR_PREC_FORMAT == 2 +#define MPFR_PREC_FORMAT_TYPE "" +#define MPFR_PREC_FORMAT_SIZE 0 +#elif _MPFR_PREC_FORMAT == 3 +#define MPFR_PREC_FORMAT_TYPE "l" +#define MPFR_PREC_FORMAT_SIZE 1 +#else +#error "mpfr_prec_t size not supported" +#endif + +/* Output for special values defined in the C99 standard */ +#define MPFR_NAN_STRING_LC "nan" +#define MPFR_NAN_STRING_UC "NAN" +#define MPFR_NAN_STRING_LENGTH 3 +#define MPFR_INF_STRING_LC "inf" +#define MPFR_INF_STRING_UC "INF" +#define MPFR_INF_STRING_LENGTH 3 + +/* The implicit \0 is useless, but we do not write num_to_text[16] + otherwise g++ complains. */ +static const char num_to_text[] = "0123456789abcdef"; + +/* some macro and functions for parsing format string */ + +/* Read an integer; saturate to INT_MAX. */ +#define READ_INT(ap, format, specinfo, field, label_out) \ + do { \ + while (*(format)) \ + { \ + int _i; \ + switch (*(format)) \ + { \ + case '0': \ + case '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9': \ + specinfo.field = (specinfo.field <= INT_MAX / 10) ? \ + specinfo.field * 10 : INT_MAX; \ + _i = *(format) - '0'; \ + MPFR_ASSERTN (_i >= 0 && _i <= 9); \ + specinfo.field = (specinfo.field <= INT_MAX - _i) ? \ + specinfo.field + _i : INT_MAX; \ + ++(format); \ + break; \ + case '*': \ + specinfo.field = va_arg ((ap), int); \ + ++(format); \ + default: \ + goto label_out; \ + } \ + } \ + } while (0) + +/* arg_t contains all the types described by the 'type' field of the + format string */ +enum arg_t + { + NONE, + CHAR_ARG, + SHORT_ARG, + LONG_ARG, + LONG_LONG_ARG, + INTMAX_ARG, + SIZE_ARG, + PTRDIFF_ARG, + LONG_DOUBLE_ARG, + MPF_ARG, + MPQ_ARG, + MP_LIMB_ARG, + MP_LIMB_ARRAY_ARG, + MPZ_ARG, + MPFR_PREC_ARG, + MPFR_ARG, + UNSUPPORTED + }; + +/* Each conversion specification of the format string will be translated in a + printf_spec structure by the parser. + This structure is adapted from the GNU libc one. */ +struct printf_spec +{ + unsigned int alt:1; /* # flag */ + unsigned int space:1; /* Space flag */ + unsigned int left:1; /* - flag */ + unsigned int showsign:1; /* + flag */ + unsigned int group:1; /* ' flag */ + + int width; /* Width */ + int prec; /* Precision */ + + enum arg_t arg_type; /* Type of argument */ + mpfr_rnd_t rnd_mode; /* Rounding mode */ + char spec; /* Conversion specifier */ + + char pad; /* Padding character */ +}; + +static void +specinfo_init (struct printf_spec *specinfo) +{ + specinfo->alt = 0; + specinfo->space = 0; + specinfo->left = 0; + specinfo->showsign = 0; + specinfo->group = 0; + specinfo->width = 0; + specinfo->prec = 0; + specinfo->arg_type = NONE; + specinfo->rnd_mode = MPFR_RNDN; + specinfo->spec = '\0'; + specinfo->pad = ' '; +} + +#define FLOATING_POINT_ARG_TYPE(at) \ + ((at) == MPFR_ARG || (at) == MPF_ARG || (at) == LONG_DOUBLE_ARG) + +#define INTEGER_LIKE_ARG_TYPE(at) \ + ((at) == SHORT_ARG || (at) == LONG_ARG || (at) == LONG_LONG_ARG \ + || (at) == INTMAX_ARG || (at) == MPFR_PREC_ARG || (at) == MPZ_ARG \ + || (at) == MPQ_ARG || (at) == MP_LIMB_ARG || (at) == MP_LIMB_ARRAY_ARG \ + || (at) == CHAR_ARG || (at) == SIZE_ARG || (at) == PTRDIFF_ARG) + +static int +specinfo_is_valid (struct printf_spec spec) +{ + switch (spec.spec) + { + case 'n': + return -1; + + case 'a': case 'A': + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + return (spec.arg_type == NONE + || FLOATING_POINT_ARG_TYPE (spec.arg_type)); + + case 'b': + return spec.arg_type == MPFR_ARG; + + case 'd': case 'i': + case 'u': case 'o': + case 'x': case 'X': + return (spec.arg_type == NONE + || INTEGER_LIKE_ARG_TYPE (spec.arg_type)); + + case 'c': + case 's': + return (spec.arg_type == NONE || spec.arg_type == LONG_ARG); + + case 'p': + return spec.arg_type == NONE; + + default: + return 0; + } +} + +static const char * +parse_flags (const char *format, struct printf_spec *specinfo) +{ + while (*format) + { + switch (*format) + { + case '0': + specinfo->pad = '0'; + ++format; + break; + case '#': + specinfo->alt = 1; + ++format; + break; + case '+': + specinfo->showsign = 1; + ++format; + break; + case ' ': + specinfo->space = 1; + ++format; + break; + case '-': + specinfo->left = 1; + ++format; + break; + case '\'': + /* Single UNIX Specification for thousand separator */ + specinfo->group = 1; + ++format; + break; + default: + return format; + } + } + return format; +} + +static const char * +parse_arg_type (const char *format, struct printf_spec *specinfo) +{ + switch (*format) + { + case '\0': + break; + case 'h': + if (*++format == 'h') +#ifndef NPRINTF_HH + { + ++format; + specinfo->arg_type = CHAR_ARG; + } +#else + specinfo->arg_type = UNSUPPORTED; +#endif + else + specinfo->arg_type = SHORT_ARG; + break; + case 'l': + if (*++format == 'l') + { + ++format; +#if defined (HAVE_LONG_LONG) && !defined(NPRINTF_LL) + specinfo->arg_type = LONG_LONG_ARG; +#else + specinfo->arg_type = UNSUPPORTED; +#endif + break; + } + else + { + specinfo->arg_type = LONG_ARG; + break; + } + case 'j': + ++format; +#if defined(_MPFR_H_HAVE_INTMAX_T) && !defined(NPRINTF_J) + specinfo->arg_type = INTMAX_ARG; +#else + specinfo->arg_type = UNSUPPORTED; +#endif + break; + case 'z': + ++format; + specinfo->arg_type = SIZE_ARG; + break; + case 't': + ++format; +#ifndef NPRINTF_T + specinfo->arg_type = PTRDIFF_ARG; +#else + specinfo->arg_type = UNSUPPORTED; +#endif + break; + case 'L': + ++format; +#ifndef NPRINTF_L + specinfo->arg_type = LONG_DOUBLE_ARG; +#else + specinfo->arg_type = UNSUPPORTED; +#endif + break; + case 'F': + ++format; + specinfo->arg_type = MPF_ARG; + break; + case 'Q': + ++format; + specinfo->arg_type = MPQ_ARG; + break; + case 'M': + ++format; + /* The 'M' specifier was added in gmp 4.2.0 */ + specinfo->arg_type = MP_LIMB_ARG; + break; + case 'N': + ++format; + specinfo->arg_type = MP_LIMB_ARRAY_ARG; + break; + case 'Z': + ++format; + specinfo->arg_type = MPZ_ARG; + break; + + /* mpfr specific specifiers */ + case 'P': + ++format; + specinfo->arg_type = MPFR_PREC_ARG; + break; + case 'R': + ++format; + specinfo->arg_type = MPFR_ARG; + } + return format; +} + + +/* some macros and functions filling the buffer */ + +/* CONSUME_VA_ARG removes from va_list AP the type expected by SPECINFO */ + +/* With a C++ compiler wchar_t and enumeration in va_list are converted to + integer type : int, unsigned int, long or unsigned long (unfortunately, + this is implementation dependant). + We follow gmp which assumes in print/doprnt.c that wchar_t is converted + to int (because wchar_t <= int). + For wint_t, we assume that the case WINT_MAX < INT_MAX yields an + integer promotion. */ +#ifdef HAVE_WCHAR_H +#if defined(WINT_MAX) && WINT_MAX < INT_MAX +typedef int mpfr_va_wint; /* integer promotion */ +#else +typedef wint_t mpfr_va_wint; +#endif +#define CASE_LONG_ARG(specinfo, ap) \ + case LONG_ARG: \ + if (((specinfo).spec == 'd') || ((specinfo).spec == 'i') \ + || ((specinfo).spec == 'o') || ((specinfo).spec == 'u') \ + || ((specinfo).spec == 'x') || ((specinfo).spec == 'X')) \ + (void) va_arg ((ap), long); \ + else if ((specinfo).spec == 'c') \ + (void) va_arg ((ap), mpfr_va_wint); \ + else if ((specinfo).spec == 's') \ + (void) va_arg ((ap), int); /* we assume integer promotion */ \ + break; +#else +#define CASE_LONG_ARG(specinfo, ap) \ + case LONG_ARG: \ + (void) va_arg ((ap), long); \ + break; +#endif + +#if defined(_MPFR_H_HAVE_INTMAX_T) +#define CASE_INTMAX_ARG(specinfo, ap) \ + case INTMAX_ARG: \ + (void) va_arg ((ap), intmax_t); \ + break; +#else +#define CASE_INTMAX_ARG(specinfo, ap) +#endif + +#ifdef HAVE_LONG_LONG +#define CASE_LONG_LONG_ARG(specinfo, ap) \ + case LONG_LONG_ARG: \ + (void) va_arg ((ap), long long); \ + break; +#else +#define CASE_LONG_LONG_ARG(specinfo, ap) +#endif + +#define CONSUME_VA_ARG(specinfo, ap) \ + do { \ + switch ((specinfo).arg_type) \ + { \ + case CHAR_ARG: \ + case SHORT_ARG: \ + (void) va_arg ((ap), int); \ + break; \ + CASE_LONG_ARG (specinfo, ap) \ + CASE_LONG_LONG_ARG (specinfo, ap) \ + CASE_INTMAX_ARG (specinfo, ap) \ + case SIZE_ARG: \ + (void) va_arg ((ap), size_t); \ + break; \ + case PTRDIFF_ARG: \ + (void) va_arg ((ap), ptrdiff_t); \ + break; \ + case LONG_DOUBLE_ARG: \ + (void) va_arg ((ap), long double); \ + break; \ + case MPF_ARG: \ + (void) va_arg ((ap), mpf_srcptr); \ + break; \ + case MPQ_ARG: \ + (void) va_arg ((ap), mpq_srcptr); \ + break; \ + case MP_LIMB_ARG: \ + (void) va_arg ((ap), mp_limb_t); \ + break; \ + case MP_LIMB_ARRAY_ARG: \ + (void) va_arg ((ap), mpfr_limb_ptr); \ + (void) va_arg ((ap), mp_size_t); \ + break; \ + case MPZ_ARG: \ + (void) va_arg ((ap), mpz_srcptr); \ + break; \ + default: \ + switch ((specinfo).spec) \ + { \ + case 'd': \ + case 'i': \ + case 'o': \ + case 'u': \ + case 'x': \ + case 'X': \ + case 'c': \ + (void) va_arg ((ap), int); \ + break; \ + case 'f': \ + case 'F': \ + case 'e': \ + case 'E': \ + case 'g': \ + case 'G': \ + case 'a': \ + case 'A': \ + (void) va_arg ((ap), double); \ + break; \ + case 's': \ + (void) va_arg ((ap), char *); \ + break; \ + case 'p': \ + (void) va_arg ((ap), void *); \ + } \ + } \ + } while (0) + +/* process the format part which does not deal with mpfr types, + jump to external label 'error' if gmp_asprintf return -1. */ +#define FLUSH(flag, start, end, ap, buf_ptr) \ + do { \ + const size_t n = (end) - (start); \ + if ((flag)) \ + /* previous specifiers are understood by gmp_printf */ \ + { \ + MPFR_TMP_DECL (marker); \ + char *fmt_copy; \ + MPFR_TMP_MARK (marker); \ + fmt_copy = (char*) MPFR_TMP_ALLOC (n + 1); \ + strncpy (fmt_copy, (start), n); \ + fmt_copy[n] = '\0'; \ + if (sprntf_gmp ((buf_ptr), (fmt_copy), (ap)) == -1) \ + { \ + MPFR_TMP_FREE (marker); \ + goto error; \ + } \ + (flag) = 0; \ + MPFR_TMP_FREE (marker); \ + } \ + else if ((start) != (end)) \ + /* no conversion specification, just simple characters */ \ + buffer_cat ((buf_ptr), (start), n); \ + } while (0) + +struct string_buffer +{ + char *start; /* beginning of the buffer */ + char *curr; /* null terminating character */ + size_t size; /* buffer capacity */ +}; + +static void +buffer_init (struct string_buffer *b, size_t s) +{ + b->start = (char *) (*__gmp_allocate_func) (s); + b->start[0] = '\0'; + b->curr = b->start; + b->size = s; +} + +/* Increase buffer size by a number of character being the least multiple of + 4096 greater than LEN+1. */ +static void +buffer_widen (struct string_buffer *b, size_t len) +{ + const size_t pos = b->curr - b->start; + const size_t n = 0x1000 + (len & ~((size_t) 0xfff)); + MPFR_ASSERTD (pos < b->size); + + MPFR_ASSERTN ((len & ~((size_t) 4095)) <= (size_t)(SIZE_MAX - 4096)); + MPFR_ASSERTN (b->size < SIZE_MAX - n); + + b->start = + (char *) (*__gmp_reallocate_func) (b->start, b->size, b->size + n); + b->size += n; + b->curr = b->start + pos; + + MPFR_ASSERTD (pos < b->size); + MPFR_ASSERTD (*b->curr == '\0'); +} + +/* Concatenate the LEN first characters of the string S to the buffer B and + expand it if needed. */ +static void +buffer_cat (struct string_buffer *b, const char *s, size_t len) +{ + MPFR_ASSERTD (len != 0); + MPFR_ASSERTD (len <= strlen (s)); + + if (MPFR_UNLIKELY ((b->curr + len) >= (b->start + b->size))) + buffer_widen (b, len); + + strncat (b->curr, s, len); + b->curr += len; + + MPFR_ASSERTD (b->curr < b->start + b->size); + MPFR_ASSERTD (*b->curr == '\0'); +} + +/* Add N characters C to the end of buffer B */ +static void +buffer_pad (struct string_buffer *b, const char c, const size_t n) +{ + MPFR_ASSERTD (n != 0); + + MPFR_ASSERTN (b->size < SIZE_MAX - n - 1); + if (MPFR_UNLIKELY ((b->curr + n + 1) > (b->start + b->size))) + buffer_widen (b, n); + + if (n == 1) + *b->curr = c; + else + memset (b->curr, c, n); + b->curr += n; + *b->curr = '\0'; + + MPFR_ASSERTD (b->curr < b->start + b->size); +} + +/* Form a string by concatenating the first LEN characters of STR to TZ + zero(s), insert into one character C each 3 characters starting from end + to begining and concatenate the result to the buffer B. */ +static void +buffer_sandwich (struct string_buffer *b, char *str, size_t len, + const size_t tz, const char c) +{ + const size_t step = 3; + const size_t size = len + tz; + const size_t r = size % step == 0 ? step : size % step; + const size_t q = size % step == 0 ? size / step - 1 : size / step; + size_t i; + + MPFR_ASSERTD (size != 0); + if (c == '\0') + { + buffer_cat (b, str, len); + buffer_pad (b, '0', tz); + return; + } + + MPFR_ASSERTN (b->size < SIZE_MAX - size - 1 - q); + MPFR_ASSERTD (len <= strlen (str)); + if (MPFR_UNLIKELY ((b->curr + size + 1 + q) > (b->start + b->size))) + buffer_widen (b, size + q); + + /* first R significant digits */ + memcpy (b->curr, str, r); + b->curr += r; + str += r; + len -= r; + + /* blocks of thousands. Warning: STR might end in the middle of a block */ + for (i = 0; i < q; ++i) + { + *b->curr++ = c; + if (MPFR_LIKELY (len > 0)) + { + if (MPFR_LIKELY (len >= step)) + /* step significant digits */ + { + memcpy (b->curr, str, step); + len -= step; + } + else + /* last digits in STR, fill up thousand block with zeros */ + { + memcpy (b->curr, str, len); + memset (b->curr + len, '0', step - len); + len = 0; + } + } + else + /* trailing zeros */ + memset (b->curr, '0', step); + + b->curr += step; + str += step; + } + + *b->curr = '\0'; + + MPFR_ASSERTD (b->curr < b->start + b->size); +} + +/* let gmp_xprintf process the part it can understand */ +static int +sprntf_gmp (struct string_buffer *b, const char *fmt, va_list ap) +{ + int length; + char *s; + + length = gmp_vasprintf (&s, fmt, ap); + if (length > 0) + buffer_cat (b, s, length); + + mpfr_free_str (s); + return length; +} + +/* Helper struct and functions for temporary strings management */ +/* struct for easy string clearing */ +struct string_list +{ + char *string; + struct string_list *next; /* NULL in last node */ +}; + +/* initialisation */ +static void +init_string_list (struct string_list *sl) +{ + sl->string = NULL; + sl->next = NULL; +} + +/* clear all strings in the list */ +static void +clear_string_list (struct string_list *sl) +{ + struct string_list *n; + + while (sl) + { + if (sl->string) + mpfr_free_str (sl->string); + n = sl->next; + (*__gmp_free_func) (sl, sizeof(struct string_list)); + sl = n; + } +} + +/* add a string in the list */ +static char * +register_string (struct string_list *sl, char *new_string) +{ + /* look for the last node */ + while (sl->next) + sl = sl->next; + + sl->next = (struct string_list*) + (*__gmp_allocate_func) (sizeof (struct string_list)); + + sl = sl->next; + sl->next = NULL; + return sl->string = new_string; +} + +/* padding type: where are the padding characters */ +enum pad_t + { + LEFT, /* spaces in left hand side for right justification */ + LEADING_ZEROS, /* padding with '0' characters in integral part */ + RIGHT /* spaces in right hand side for left justification */ + }; + +/* number_parts details how much characters are needed in each part of a float + print. */ +struct number_parts +{ + enum pad_t pad_type; /* Padding type */ + size_t pad_size; /* Number of padding characters */ + + char sign; /* Sign character */ + + char *prefix_ptr; /* Pointer to prefix part */ + size_t prefix_size; /* Number of characters in *prefix_ptr */ + + char thousands_sep; /* Thousands separator (only with style 'f') */ + + char *ip_ptr; /* Pointer to integral part characters*/ + size_t ip_size; /* Number of digits in *ip_ptr */ + int ip_trailing_zeros; /* Number of additional null digits in integral + part */ + + char point; /* Decimal point character */ + + int fp_leading_zeros; /* Number of additional leading zeros in fractional + part */ + char *fp_ptr; /* Pointer to fractional part characters */ + size_t fp_size; /* Number of digits in *fp_ptr */ + int fp_trailing_zeros; /* Number of additional trailing zeros in fractional + part */ + + char *exp_ptr; /* Pointer to exponent part */ + size_t exp_size; /* Number of characters in *exp_ptr */ + + struct string_list *sl; /* List of string buffers in use: we need such a + mechanism because fp_ptr may point into the same + string as ip_ptr */ +}; + +/* For a real non zero number x, what is the base exponent f when rounding x + with rounding mode r to r(x) = m*b^f, where m is a digit and 1 <= m < b ? + Return non zero value if x is rounded up to b^f, return zero otherwise */ +static int +next_base_power_p (mpfr_srcptr x, int base, mpfr_rnd_t rnd) +{ + mpfr_prec_t nbits; + mp_limb_t pm; + mp_limb_t xm; + + MPFR_ASSERTD (MPFR_IS_PURE_FP (x)); + MPFR_ASSERTD (base == 2 || base == 16); + + /* Warning: the decimal point is AFTER THE FIRST DIGIT in this output + representation. */ + nbits = base == 2 ? 1 : 4; + + if (rnd == MPFR_RNDZ + || (rnd == MPFR_RNDD && MPFR_IS_POS (x)) + || (rnd == MPFR_RNDU && MPFR_IS_NEG (x)) + || MPFR_PREC (x) <= nbits) + /* no rounding when printing x with 1 digit */ + return 0; + + xm = MPFR_MANT (x) [MPFR_LIMB_SIZE (x) - 1]; + pm = MPFR_LIMB_MASK (GMP_NUMB_BITS - nbits); + if ((xm & ~pm) ^ ~pm) + /* do no round up if some of the nbits first bits are 0s. */ + return 0; + + if (rnd == MPFR_RNDN) + /* mask for rounding bit */ + pm = (MPFR_LIMB_ONE << (GMP_NUMB_BITS - nbits - 1)); + + /* round up if some remaining bits are 1 */ + /* warning: the return value must be an int */ + return xm & pm ? 1 : 0; +} + +/* Record information from mpfr_get_str() so as to avoid multiple + calls to this expensive function. */ +struct decimal_info +{ + mpfr_exp_t exp; + char *str; +}; + +/* For a real non zero number x, what is the exponent f so that + 10^f <= x < 10^(f+1). */ +static mpfr_exp_t +floor_log10 (mpfr_srcptr x) +{ + mpfr_t y; + mpfr_exp_t exp; + + /* make sure first that y can represent a mpfr_exp_t exactly + and can compare with x */ + mpfr_prec_t prec = sizeof (mpfr_exp_t) * CHAR_BIT; + mpfr_init2 (y, MAX (prec, MPFR_PREC (x))); + + exp = mpfr_ceil_mul (MPFR_GET_EXP (x), 10, 1) - 1; + mpfr_set_exp_t (y, exp, MPFR_RNDU); + /* The following call to mpfr_ui_pow should be fast: y is an integer + (not too large), so that mpfr_pow_z will be used internally. */ + mpfr_ui_pow (y, 10, y, MPFR_RNDU); + if (mpfr_cmpabs (x, y) < 0) + exp--; + + mpfr_clear (y); + return exp; +} + +/* Determine the different parts of the string representation of the regular + number P when SPEC.SPEC is 'a', 'A', or 'b'. + + return -1 if some field > INT_MAX */ +static int +regular_ab (struct number_parts *np, mpfr_srcptr p, + const struct printf_spec spec) +{ + int uppercase; + int base; + char *str; + mpfr_exp_t exp; + + uppercase = spec.spec == 'A'; + + /* sign */ + if (MPFR_IS_NEG (p)) + np->sign = '-'; + else if (spec.showsign || spec.space) + np->sign = spec.showsign ? '+' : ' '; + + if (spec.spec == 'a' || spec.spec == 'A') + /* prefix part */ + { + np->prefix_size = 2; + str = (char *) (*__gmp_allocate_func) (1 + np->prefix_size); + str[0] = '0'; + str[1] = uppercase ? 'X' : 'x'; + str[2] = '\0'; + np->prefix_ptr = register_string (np->sl, str); + } + + /* integral part */ + np->ip_size = 1; + base = (spec.spec == 'b') ? 2 : 16; + + if (spec.prec != 0) + { + size_t nsd; + + /* Number of significant digits: + - if no given precision, let mpfr_get_str determine it; + - if a non-zero precision is specified, then one digit before decimal + point plus SPEC.PREC after it. */ + nsd = spec.prec < 0 ? 0 : spec.prec + np->ip_size; + str = mpfr_get_str (0, &exp, base, nsd, p, spec.rnd_mode); + register_string (np->sl, str); + np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign if any */ + + if (base == 16) + /* EXP is the exponent for radix sixteen with decimal point BEFORE the + first digit, we want the exponent for radix two and the decimal + point AFTER the first digit. */ + { + MPFR_ASSERTN (exp > MPFR_EMIN_MIN /4); /* possible overflow */ + exp = (exp - 1) * 4; + } + else + /* EXP is the exponent for decimal point BEFORE the first digit, we + want the exponent for decimal point AFTER the first digit. */ + { + MPFR_ASSERTN (exp > MPFR_EMIN_MIN); /* possible overflow */ + --exp; + } + } + else if (next_base_power_p (p, base, spec.rnd_mode)) + { + str = (char *)(*__gmp_allocate_func) (2); + str[0] = '1'; + str[1] = '\0'; + np->ip_ptr = register_string (np->sl, str); + + exp = MPFR_GET_EXP (p); + } + else if (base == 2) + { + str = (char *)(*__gmp_allocate_func) (2); + str[0] = '1'; + str[1] = '\0'; + np->ip_ptr = register_string (np->sl, str); + + exp = MPFR_GET_EXP (p) - 1; + } + else + { + int digit; + mp_limb_t msl = MPFR_MANT (p)[MPFR_LIMB_SIZE (p) - 1]; + int rnd_bit = GMP_NUMB_BITS - 5; + + /* pick up the 4 first bits */ + digit = msl >> (rnd_bit+1); + if (spec.rnd_mode == MPFR_RNDA + || (spec.rnd_mode == MPFR_RNDU && MPFR_IS_POS (p)) + || (spec.rnd_mode == MPFR_RNDD && MPFR_IS_NEG (p)) + || (spec.rnd_mode == MPFR_RNDN + && (msl & (MPFR_LIMB_ONE << rnd_bit)))) + digit++; + MPFR_ASSERTD ((0 <= digit) && (digit <= 15)); + + str = (char *)(*__gmp_allocate_func) (1 + np->ip_size); + str[0] = num_to_text [digit]; + str[1] = '\0'; + np->ip_ptr = register_string (np->sl, str); + + exp = MPFR_GET_EXP (p) - 4; + } + + if (uppercase) + /* All digits in upper case */ + { + char *s1 = str; + while (*s1) + { + switch (*s1) + { + case 'a': + *s1 = 'A'; + break; + case 'b': + *s1 = 'B'; + break; + case 'c': + *s1 = 'C'; + break; + case 'd': + *s1 = 'D'; + break; + case 'e': + *s1 = 'E'; + break; + case 'f': + *s1 = 'F'; + break; + } + s1++; + } + } + + if (spec.spec == 'b' || spec.prec != 0) + /* compute the number of digits in fractional part */ + { + char *ptr; + size_t str_len; + + /* the sign has been skipped, skip also the first digit */ + ++str; + str_len = strlen (str); + ptr = str + str_len - 1; /* points to the end of str */ + + if (spec.prec < 0) + /* remove trailing zeros, if any */ + { + while ((*ptr == '0') && (str_len != 0)) + { + --ptr; + --str_len; + } + } + + if (str_len > INT_MAX) + /* too many digits in fractional part */ + return -1; + + if (str_len != 0) + /* there are some non-zero digits in fractional part */ + { + np->fp_ptr = str; + np->fp_size = str_len; + if ((int) str_len < spec.prec) + np->fp_trailing_zeros = spec.prec - str_len; + } + } + + /* decimal point */ + if ((np->fp_size != 0) || spec.alt) + np->point = MPFR_DECIMAL_POINT; + + /* the exponent part contains the character 'p', or 'P' plus the sign + character plus at least one digit and only as many more digits as + necessary to represent the exponent. + We assume that |EXP| < 10^INT_MAX. */ + np->exp_size = 3; + { + mpfr_uexp_t x; + + x = SAFE_ABS (mpfr_uexp_t, exp); + while (x > 9) + { + np->exp_size++; + x /= 10; + } + } + str = (char *) (*__gmp_allocate_func) (1 + np->exp_size); + np->exp_ptr = register_string (np->sl, str); + { + char exp_fmt[8]; /* contains at most 7 characters like in "p%+.1i", + or "P%+.2li" */ + + exp_fmt[0] = uppercase ? 'P' : 'p'; + exp_fmt[1] = '\0'; + strcat (exp_fmt, "%+.1" MPFR_EXP_FSPEC "d"); + + if (sprintf (str, exp_fmt, (mpfr_eexp_t) exp) < 0) + return -1; + } + + return 0; +} + +/* Determine the different parts of the string representation of the regular + number P when SPEC.SPEC is 'e', 'E', 'g', or 'G'. + DEC_INFO contains the previously computed exponent and string or is NULL. + + return -1 if some field > INT_MAX */ +static int +regular_eg (struct number_parts *np, mpfr_srcptr p, + const struct printf_spec spec, struct decimal_info *dec_info) +{ + char *str; + mpfr_exp_t exp; + + const int uppercase = spec.spec == 'E' || spec.spec == 'G'; + const int spec_g = spec.spec == 'g' || spec.spec == 'G'; + const int keep_trailing_zeros = (spec_g && spec.alt) + || (!spec_g && (spec.prec > 0)); + + /* sign */ + if (MPFR_IS_NEG (p)) + np->sign = '-'; + else if (spec.showsign || spec.space) + np->sign = spec.showsign ? '+' : ' '; + + /* integral part */ + np->ip_size = 1; + if (dec_info == NULL) + { + size_t nsd; + + /* Number of significant digits: + - if no given precision, then let mpfr_get_str determine it, + - if a precision is specified, then one digit before decimal point + plus SPEC.PREC after it. + We use the fact here that mpfr_get_str allows us to ask for only one + significant digit when the base is not a power of 2. */ + nsd = (spec.prec < 0) ? 0 : spec.prec + np->ip_size; + str = mpfr_get_str (0, &exp, 10, nsd, p, spec.rnd_mode); + register_string (np->sl, str); + } + else + { + exp = dec_info->exp; + str = dec_info->str; + } + np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign if any */ + + if (spec.prec != 0) + /* compute the number of digits in fractional part */ + { + char *ptr; + size_t str_len; + + /* the sign has been skipped, skip also the first digit */ + ++str; + str_len = strlen (str); + ptr = str + str_len - 1; /* points to the end of str */ + + if (!keep_trailing_zeros) + /* remove trailing zeros, if any */ + { + while ((*ptr == '0') && (str_len != 0)) + { + --ptr; + --str_len; + } + } + + if (str_len > INT_MAX) + /* too many digits in fractional part */ + return -1; + + if (str_len != 0) + /* there are some non-zero digits in fractional part */ + { + np->fp_ptr = str; + np->fp_size = str_len; + if ((!spec_g || spec.alt) && (spec.prec > 0) + && ((int)str_len < spec.prec)) + /* add missing trailing zeros */ + np->fp_trailing_zeros = spec.prec - str_len; + } + } + + /* decimal point */ + if (np->fp_size != 0 || spec.alt) + np->point = MPFR_DECIMAL_POINT; + + /* EXP is the exponent for decimal point BEFORE the first digit, we want + the exponent for decimal point AFTER the first digit. + Here, no possible overflow because exp < MPFR_EXP (p) / 3 */ + exp--; + + /* the exponent part contains the character 'e', or 'E' plus the sign + character plus at least two digits and only as many more digits as + necessary to represent the exponent. + We assume that |EXP| < 10^INT_MAX. */ + np->exp_size = 3; + { + mpfr_uexp_t x; + + x = SAFE_ABS (mpfr_uexp_t, exp); + while (x > 9) + { + np->exp_size++; + x /= 10; + } + } + if (np->exp_size < 4) + np->exp_size = 4; + + str = (char *) (*__gmp_allocate_func) (1 + np->exp_size); + np->exp_ptr = register_string (np->sl, str); + + { + char exp_fmt[8]; /* e.g. "e%+.2i", or "E%+.2li" */ + + exp_fmt[0] = uppercase ? 'E' : 'e'; + exp_fmt[1] = '\0'; + strcat (exp_fmt, "%+.2" MPFR_EXP_FSPEC "d"); + + if (sprintf (str, exp_fmt, (mpfr_eexp_t) exp) < 0) + return -1; + } + + return 0; +} + +/* Determine the different parts of the string representation of the regular + number P when SPEC.SPEC is 'f', 'F', 'g', or 'G'. + DEC_INFO contains the previously computed exponent and string or is NULL. + + return -1 if some field of number_parts is greater than INT_MAX */ +static int +regular_fg (struct number_parts *np, mpfr_srcptr p, + const struct printf_spec spec, struct decimal_info *dec_info) +{ + mpfr_exp_t exp; + char * str; + const int spec_g = (spec.spec == 'g' || spec.spec == 'G'); + const int keep_trailing_zeros = !spec_g || spec.alt; + + /* WARNING: an empty precision field is forbidden (it means precision = 6 + and it should have been changed to 6 before the function call) */ + MPFR_ASSERTD (spec.prec >= 0); + + /* sign */ + if (MPFR_IS_NEG (p)) + np->sign = '-'; + else if (spec.showsign || spec.space) + np->sign = spec.showsign ? '+' : ' '; + + if (MPFR_GET_EXP (p) <= 0) + /* 0 < |p| < 1 */ + { + /* Most of the time, integral part is 0 */ + np->ip_size = 1; + str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); + str[0] = '0'; + str[1] = '\0'; + np->ip_ptr = register_string (np->sl, str); + + if (spec.prec == 0) + /* only two possibilities: either 1 or 0. */ + { + mpfr_t y; + /* y = abs(p) */ + MPFR_ALIAS (y, p, 1, MPFR_EXP (p)); + + if (spec.rnd_mode == MPFR_RNDA + || (spec.rnd_mode == MPFR_RNDD && MPFR_IS_NEG (p)) + || (spec.rnd_mode == MPFR_RNDU && MPFR_IS_POS (p)) + || (spec.rnd_mode == MPFR_RNDN && mpfr_cmp_d (y, 0.5) > 0)) + /* rounded up to 1: one digit '1' in integral part. + note that 0.5 is rounded to 0 with RNDN (round ties to even) */ + np->ip_ptr[0] = '1'; + } + else + { + /* exp = position of the most significant decimal digit. */ + exp = floor_log10 (p); + MPFR_ASSERTD (exp < 0); + + if (exp < -spec.prec) + /* only the last digit may be non zero */ + { + int round_away; + switch (spec.rnd_mode) + { + case MPFR_RNDA: + round_away = 1; + break; + case MPFR_RNDD: + round_away = MPFR_IS_NEG (p); + break; + case MPFR_RNDU: + round_away = MPFR_IS_POS (p); + break; + case MPFR_RNDN: + { + /* compare |p| to y = 0.5*10^(-spec.prec) */ + mpfr_t y; + mpfr_exp_t e = MAX (MPFR_PREC (p), 56); + mpfr_init2 (y, e + 8); + do + { + /* find a lower approximation of + 0.5*10^(-spec.prec) different from |p| */ + e += 8; + mpfr_set_prec (y, e); + mpfr_set_si (y, -spec.prec, MPFR_RNDN); + mpfr_exp10 (y, y, MPFR_RNDD); + mpfr_div_2ui (y, y, 1, MPFR_RNDN); + } while (mpfr_cmpabs (y, p) == 0); + + round_away = mpfr_cmpabs (y, p) < 0; + mpfr_clear (y); + } + break; + default: + round_away = 0; + } + + if (round_away) + /* round away from zero: the last output digit is '1' */ + { + np->fp_leading_zeros = spec.prec - 1; + + np->fp_size = 1; + str = + (char *) (*__gmp_allocate_func) (1 + np->fp_size); + str[0] = '1'; + str[1] = '\0'; + np->fp_ptr = register_string (np->sl, str); + } + else + /* only zeros in fractional part */ + { + MPFR_ASSERTD (!spec_g); + np->fp_leading_zeros = spec.prec; + } + } + else + /* the most significant digits are the last + spec.prec + exp + 1 digits in fractional part */ + { + char *ptr; + size_t str_len; + if (dec_info == NULL) + { + size_t nsd = spec.prec + exp + 1; + /* WARNING: nsd may equal 1, but here we use the + fact that mpfr_get_str can return one digit with + base ten (undocumented feature, see comments in + get_str.c) */ + + str = mpfr_get_str (NULL, &exp, 10, nsd, p, spec.rnd_mode); + register_string (np->sl, str); + } + else + { + exp = dec_info->exp; + str = dec_info->str; + } + if (MPFR_IS_NEG (p)) + /* skip sign */ + ++str; + if (exp == 1) + /* round up to 1 */ + { + MPFR_ASSERTD (str[0] == '1'); + np->ip_ptr[0] = '1'; + if (!spec_g || spec.alt) + np->fp_leading_zeros = spec.prec; + } + else + { + np->fp_ptr = str; + np->fp_leading_zeros = -exp; + MPFR_ASSERTD (exp <= 0); + + str_len = strlen (str); /* the sign has been skipped */ + ptr = str + str_len - 1; /* points to the end of str */ + + if (!keep_trailing_zeros) + /* remove trailing zeros, if any */ + { + while ((*ptr == '0') && str_len) + { + --ptr; + --str_len; + } + } + + if (str_len > INT_MAX) + /* too many digits in fractional part */ + return -1; + + MPFR_ASSERTD (str_len > 0); + np->fp_size = str_len; + + if ((!spec_g || spec.alt) + && spec.prec > 0 + && (np->fp_leading_zeros + np->fp_size < spec.prec)) + /* add missing trailing zeros */ + np->fp_trailing_zeros = spec.prec - np->fp_leading_zeros + - np->fp_size; + } + } + } + + if (spec.alt || np->fp_leading_zeros != 0 || np->fp_size != 0 + || np->fp_trailing_zeros != 0) + np->point = MPFR_DECIMAL_POINT; + } + else + /* 1 <= |p| */ + { + size_t str_len; + + /* Determine the position of the most significant decimal digit. */ + exp = floor_log10 (p); + MPFR_ASSERTD (exp >= 0); + if (exp > INT_MAX) + /* P is too large to print all its integral part digits */ + return -1; + + if (dec_info == NULL) + { /* this case occurs with mpfr_printf ("%.0RUf", x) with x=9.5 */ + str = + mpfr_get_str (NULL, &exp, 10, spec.prec+exp+1, p, spec.rnd_mode); + register_string (np->sl, str); + } + else + { + exp = dec_info->exp; + str = dec_info->str; + } + np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign */ + str_len = strlen (str); + + /* integral part */ + if (exp > str_len) + /* mpfr_get_str gives no trailing zero when p is rounded up to the next + power of 10 (p integer, so no fractional part) */ + { + np->ip_trailing_zeros = exp - str_len; + np->ip_size = str_len; + } + else + np->ip_size = exp; + + if (spec.group) + /* thousands separator in integral part */ + np->thousands_sep = MPFR_THOUSANDS_SEPARATOR; + + /* fractional part */ + str += np->ip_size; + str_len -= np->ip_size; + if (!keep_trailing_zeros) + /* remove trailing zeros, if any */ + { + char *ptr = str + str_len - 1; /* pointer to the last digit of + str */ + while ((*ptr == '0') && (str_len != 0)) + { + --ptr; + --str_len; + } + } + + if (str_len > 0) + /* some nonzero digits in fractional part */ + { + if (str_len > INT_MAX) + /* too many digits in fractional part */ + return -1; + + np->point = MPFR_DECIMAL_POINT; + np->fp_ptr = str; + np->fp_size = str_len; + } + + if (keep_trailing_zeros && str_len < spec.prec) + /* add missing trailing zeros */ + { + np->point = MPFR_DECIMAL_POINT; + np->fp_trailing_zeros = spec.prec - np->fp_size; + } + + if (spec.alt) + /* add decimal point even if no digits follow it */ + np->point = MPFR_DECIMAL_POINT; + } + + return 0; +} + +/* partition_number determines the different parts of the string + representation of the number p according to the given specification. + partition_number initializes the given structure np, so all previous + information in that variable is lost. + return the total number of characters to be written. + return -1 if an error occured, in that case np's fields are in an undefined + state but all string buffers have been freed. */ +static int +partition_number (struct number_parts *np, mpfr_srcptr p, + struct printf_spec spec) +{ + char *str; + long total; + int uppercase; + + /* WARNING: left justification means right space padding */ + np->pad_type = spec.left ? RIGHT : spec.pad == '0' ? LEADING_ZEROS : LEFT; + np->pad_size = 0; + np->sign = '\0'; + np->prefix_ptr =NULL; + np->prefix_size = 0; + np->thousands_sep = '\0'; + np->ip_ptr = NULL; + np->ip_size = 0; + np->ip_trailing_zeros = 0; + np->point = '\0'; + np->fp_leading_zeros = 0; + np->fp_ptr = NULL; + np->fp_size = 0; + np->fp_trailing_zeros = 0; + np->exp_ptr = NULL; + np->exp_size = 0; + np->sl = (struct string_list *) + (*__gmp_allocate_func) (sizeof (struct string_list)); + init_string_list (np->sl); + + uppercase = spec.spec == 'A' || spec.spec == 'E' || spec.spec == 'F' + || spec.spec == 'G'; + + if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (p))) + { + if (MPFR_IS_NAN (p)) + { + if (np->pad_type == LEADING_ZEROS) + /* don't want "0000nan", change to right justification padding + with left spaces instead */ + np->pad_type = LEFT; + + if (uppercase) + { + np->ip_size = MPFR_NAN_STRING_LENGTH; + str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); + strcpy (str, MPFR_NAN_STRING_UC); + np->ip_ptr = register_string (np->sl, str); + } + else + { + np->ip_size = MPFR_NAN_STRING_LENGTH; + str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); + strcpy (str, MPFR_NAN_STRING_LC); + np->ip_ptr = register_string (np->sl, str); + } + } + else if (MPFR_IS_INF (p)) + { + if (np->pad_type == LEADING_ZEROS) + /* don't want "0000inf", change to right justification padding + with left spaces instead */ + np->pad_type = LEFT; + + if (MPFR_IS_NEG (p)) + np->sign = '-'; + + if (uppercase) + { + np->ip_size = MPFR_INF_STRING_LENGTH; + str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); + strcpy (str, MPFR_INF_STRING_UC); + np->ip_ptr = register_string (np->sl, str); + } + else + { + np->ip_size = MPFR_INF_STRING_LENGTH; + str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); + strcpy (str, MPFR_INF_STRING_LC); + np->ip_ptr = register_string (np->sl, str); + } + } + else + /* p == 0 */ + { + /* note: for 'g' spec, zero is always displayed with 'f'-style with + precision spec.prec - 1 and the trailing zeros are removed unless + the flag '#' is used. */ + if (MPFR_IS_NEG (p)) + /* signed zero */ + np->sign = '-'; + else if (spec.showsign || spec.space) + np->sign = spec.showsign ? '+' : ' '; + + if (spec.spec == 'a' || spec.spec == 'A') + /* prefix part */ + { + np->prefix_size = 2; + str = (char *) (*__gmp_allocate_func) (1 + np->prefix_size); + str[0] = '0'; + str[1] = uppercase ? 'X' : 'x'; + str[2] = '\0'; + np->prefix_ptr = register_string (np->sl, str); + } + + /* integral part */ + np->ip_size = 1; + str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); + str[0] = '0'; + str[1] = '\0'; + np->ip_ptr = register_string (np->sl, str); + + if (spec.prec > 0 + && ((spec.spec != 'g' && spec.spec != 'G') || spec.alt)) + /* fractional part */ + { + np->point = MPFR_DECIMAL_POINT; + np->fp_trailing_zeros = (spec.spec == 'g' && spec.spec == 'G') ? + spec.prec - 1 : spec.prec; + } + else if (spec.alt) + np->point = MPFR_DECIMAL_POINT; + + if (spec.spec == 'a' || spec.spec == 'A' || spec.spec == 'b' + || spec.spec == 'e' || spec.spec == 'E') + /* exponent part */ + { + np->exp_size = (spec.spec == 'e' || spec.spec == 'E') ? 4 : 3; + str = (char *) (*__gmp_allocate_func) (1 + np->exp_size); + if (spec.spec == 'e' || spec.spec == 'E') + strcpy (str, uppercase ? "E+00" : "e+00"); + else + strcpy (str, uppercase ? "P+0" : "p+0"); + np->exp_ptr = register_string (np->sl, str); + } + } + } + else + /* regular p, p != 0 */ + { + if (spec.spec == 'a' || spec.spec == 'A' || spec.spec == 'b') + { + if (regular_ab (np, p, spec) == -1) + goto error; + } + else if (spec.spec == 'f' || spec.spec == 'F') + { + if (spec.prec == -1) + spec.prec = 6; + if (regular_fg (np, p, spec, NULL) == -1) + goto error; + } + else if (spec.spec == 'e' || spec.spec == 'E') + { + if (regular_eg (np, p, spec, NULL) == -1) + goto error; + } + else + /* %g case */ + { + /* Use the C99 rules: + if T > X >= -4 then the conversion is with style 'f'/'F' and + precision T-(X+1). + otherwise, the conversion is with style 'e'/'E' and + precision T-1. + where T is the threshold computed below and X is the exponent + that would be displayed with style 'e' and precision T-1. */ + int threshold; + mpfr_exp_t x; + struct decimal_info dec_info; + + threshold = (spec.prec < 0) ? 6 : (spec.prec == 0) ? 1 : spec.prec; + dec_info.str = mpfr_get_str (NULL, &dec_info.exp, 10, threshold, + p, spec.rnd_mode); + register_string (np->sl, dec_info.str); + /* mpfr_get_str corresponds to a significand between 0.1 and 1, + whereas here we want a significand between 1 and 10. */ + x = dec_info.exp - 1; + + if (threshold > x && x >= -4) + { + /* the conversion is with style 'f' */ + spec.prec = threshold - x - 1; + + if (regular_fg (np, p, spec, &dec_info) == -1) + goto error; + } + else + { + spec.prec = threshold - 1; + + if (regular_eg (np, p, spec, &dec_info) == -1) + goto error; + } + } + } + + /* compute the number of characters to be written verifying it is not too + much */ + total = np->sign ? 1 : 0; + total += np->prefix_size; + total += np->ip_size; + if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) + goto error; + total += np->ip_trailing_zeros; + if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) + goto error; + if (np->thousands_sep) + /* ' flag, style f and the thousands separator in current locale is not + reduced to the null character */ + total += (np->ip_size + np->ip_trailing_zeros) / 3; + if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) + goto error; + if (np->point) + ++total; + total += np->fp_leading_zeros; + if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) + goto error; + total += np->fp_size; + if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) + goto error; + total += np->fp_trailing_zeros; + if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) + goto error; + total += np->exp_size; + if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) + goto error; + + if (spec.width > total) + /* pad with spaces or zeros depending on np->pad_type */ + { + np->pad_size = spec.width - total; + total += np->pad_size; /* here total == spec.width, + so 0 < total < INT_MAX */ + } + + return total; + + error: + clear_string_list (np->sl); + np->prefix_ptr = NULL; + np->ip_ptr = NULL; + np->fp_ptr = NULL; + np->exp_ptr = NULL; + return -1; +} + +/* sprnt_fp prints a mpfr_t according to spec.spec specification. + + return the size of the string (not counting the terminating '\0') + return -1 if the built string is too long (i.e. has more than + INT_MAX characters). */ +static int +sprnt_fp (struct string_buffer *buf, mpfr_srcptr p, + const struct printf_spec spec) +{ + int length; + struct number_parts np; + + length = partition_number (&np, p, spec); + if (length < 0) + return -1; + + /* right justification padding with left spaces */ + if (np.pad_type == LEFT && np.pad_size != 0) + buffer_pad (buf, ' ', np.pad_size); + + /* sign character (may be '-', '+', or ' ') */ + if (np.sign) + buffer_pad (buf, np.sign, 1); + + /* prefix part */ + if (np.prefix_ptr) + buffer_cat (buf, np.prefix_ptr, np.prefix_size); + + /* right justification padding with leading zeros */ + if (np.pad_type == LEADING_ZEROS && np.pad_size != 0) + buffer_pad (buf, '0', np.pad_size); + + /* integral part (may also be "nan" or "inf") */ + MPFR_ASSERTN (np.ip_ptr != NULL); /* never empty */ + if (MPFR_UNLIKELY (np.thousands_sep)) + buffer_sandwich (buf, np.ip_ptr, np.ip_size, np.ip_trailing_zeros, + np.thousands_sep); + else + { + buffer_cat (buf, np.ip_ptr, np.ip_size); + + /* trailing zeros in integral part */ + if (np.ip_trailing_zeros != 0) + buffer_pad (buf, '0', np.ip_trailing_zeros); + } + + /* decimal point */ + if (np.point) + buffer_pad (buf, np.point, 1); + + /* leading zeros in fractional part */ + if (np.fp_leading_zeros != 0) + buffer_pad (buf, '0', np.fp_leading_zeros); + + /* significant digits in fractional part */ + if (np.fp_ptr) + buffer_cat (buf, np.fp_ptr, np.fp_size); + + /* trailing zeros in fractional part */ + if (np.fp_trailing_zeros != 0) + buffer_pad (buf, '0', np.fp_trailing_zeros); + + /* exponent part */ + if (np.exp_ptr) + buffer_cat (buf, np.exp_ptr, np.exp_size); + + /* left justication padding with right spaces */ + if (np.pad_type == RIGHT && np.pad_size != 0) + buffer_pad (buf, ' ', np.pad_size); + + clear_string_list (np.sl); + return length; +} + +int +mpfr_vasprintf (char **ptr, const char *fmt, va_list ap) +{ + struct string_buffer buf; + size_t nbchar; + + /* informations on the conversion specification filled by the parser */ + struct printf_spec spec; + /* flag raised when previous part of fmt need to be processed by + gmp_vsnprintf */ + int xgmp_fmt_flag; + /* beginning and end of the previous unprocessed part of fmt */ + const char *start, *end; + /* pointer to arguments for gmp_vasprintf */ + va_list ap2; + + MPFR_SAVE_EXPO_DECL (expo); + MPFR_SAVE_EXPO_MARK (expo); + + nbchar = 0; + buffer_init (&buf, 4096); + xgmp_fmt_flag = 0; + va_copy (ap2, ap); + start = fmt; + while (*fmt) + { + /* Look for the next format specification */ + while ((*fmt) && (*fmt != '%')) + ++fmt; + + if (*fmt == '\0') + break; + + if (*++fmt == '%') + /* %%: go one step further otherwise the second '%' would be + considered as a new conversion specification introducing + character */ + { + ++fmt; + xgmp_fmt_flag = 1; + continue; + } + + end = fmt - 1; + + /* format string analysis */ + specinfo_init (&spec); + fmt = parse_flags (fmt, &spec); + + READ_INT (ap, fmt, spec, width, width_analysis); + width_analysis: + if (spec.width < 0) + { + spec.left = 1; + spec.width = -spec.width; + MPFR_ASSERTN (spec.width < INT_MAX); + } + if (*fmt == '.') + { + const char *f = ++fmt; + READ_INT (ap, fmt, spec, prec, prec_analysis); + prec_analysis: + if (f == fmt) + spec.prec = -1; + } + else + spec.prec = -1; + + fmt = parse_arg_type (fmt, &spec); + if (spec.arg_type == UNSUPPORTED) + /* the current architecture doesn't support this type */ + { + goto error; + } + else if (spec.arg_type == MPFR_ARG) + { + switch (*fmt) + { + case '\0': + break; + case '*': + ++fmt; + spec.rnd_mode = (mpfr_rnd_t) va_arg (ap, int); + break; + case 'D': + ++fmt; + spec.rnd_mode = MPFR_RNDD; + break; + case 'U': + ++fmt; + spec.rnd_mode = MPFR_RNDU; + break; + case 'Y': + ++fmt; + spec.rnd_mode = MPFR_RNDA; + break; + case 'Z': + ++fmt; + spec.rnd_mode = MPFR_RNDZ; + break; + case 'N': + ++fmt; + default: + spec.rnd_mode = MPFR_RNDN; + } + } + + spec.spec = *fmt; + if (!specinfo_is_valid (spec)) + goto error; + + if (*fmt) + fmt++; + + /* Format processing */ + if (spec.spec == '\0') + /* end of the format string */ + break; + else if (spec.spec == 'n') + /* put the number of characters written so far in the location pointed + by the next va_list argument; the types of pointer accepted are the + same as in GMP (except unsupported quad_t) plus pointer to a mpfr_t + so as to be able to accept the same format strings. */ + { + void *p; + size_t nchar; + + p = va_arg (ap, void *); + FLUSH (xgmp_fmt_flag, start, end, ap2, &buf); + va_end (ap2); + start = fmt; + nchar = buf.curr - buf.start; + + switch (spec.arg_type) + { + case CHAR_ARG: + *(char *) p = (char) nchar; + break; + case SHORT_ARG: + *(short *) p = (short) nchar; + break; + case LONG_ARG: + *(long *) p = (long) nchar; + break; +#ifdef HAVE_LONG_LONG + case LONG_LONG_ARG: + *(long long *) p = (long long) nchar; + break; +#endif +#ifdef _MPFR_H_HAVE_INTMAX_T + case INTMAX_ARG: + *(intmax_t *) p = (intmax_t) nchar; + break; +#endif + case SIZE_ARG: + *(size_t *) p = nchar; + break; + case PTRDIFF_ARG: + *(ptrdiff_t *) p = (ptrdiff_t) nchar; + break; + case MPF_ARG: + mpf_set_ui ((mpf_ptr) p, (unsigned long) nchar); + break; + case MPQ_ARG: + mpq_set_ui ((mpq_ptr) p, (unsigned long) nchar, 1L); + break; + case MP_LIMB_ARG: + *(mp_limb_t *) p = (mp_limb_t) nchar; + break; + case MP_LIMB_ARRAY_ARG: + { + mp_limb_t *q = (mp_limb_t *) p; + mp_size_t n; + n = va_arg (ap, mp_size_t); + if (n < 0) + n = -n; + else if (n == 0) + break; + + /* we assume here that mp_limb_t is wider than int */ + *q = (mp_limb_t) nchar; + while (--n != 0) + { + q++; + *q = (mp_limb_t) 0; + } + } + break; + case MPZ_ARG: + mpz_set_ui ((mpz_ptr) p, (unsigned long) nchar); + break; + + case MPFR_ARG: + mpfr_set_ui ((mpfr_ptr) p, (unsigned long) nchar, + spec.rnd_mode); + break; + + default: + *(int *) p = (int) nchar; + } + va_copy (ap2, ap); /* after the switch, due to MP_LIMB_ARRAY_ARG + case */ + } + else if (spec.arg_type == MPFR_PREC_ARG) + /* output mpfr_prec_t variable */ + { + char *s; + char format[MPFR_PREC_FORMAT_SIZE + 6]; /* see examples below */ + size_t length; + mpfr_prec_t prec; + prec = va_arg (ap, mpfr_prec_t); + + FLUSH (xgmp_fmt_flag, start, end, ap2, &buf); + va_end (ap2); + va_copy (ap2, ap); + start = fmt; + + /* construct format string, like "%*.*hu" "%*.*u" or "%*.*lu" */ + format[0] = '%'; + format[1] = '*'; + format[2] = '.'; + format[3] = '*'; + format[4] = '\0'; + strcat (format, MPFR_PREC_FORMAT_TYPE); + format[4 + MPFR_PREC_FORMAT_SIZE] = spec.spec; + format[5 + MPFR_PREC_FORMAT_SIZE] = '\0'; + length = gmp_asprintf (&s, format, spec.width, spec.prec, prec); + if (buf.size <= INT_MAX - length) + { + buffer_cat (&buf, s, length); + mpfr_free_str (s); + } + else + { + mpfr_free_str (s); + goto overflow_error; + } + } + else if (spec.arg_type == MPFR_ARG) + /* output a mpfr_t variable */ + { + mpfr_srcptr p; + + p = va_arg (ap, mpfr_srcptr); + + FLUSH (xgmp_fmt_flag, start, end, ap2, &buf); + va_end (ap2); + va_copy (ap2, ap); + start = fmt; + + switch (spec.spec) + { + case 'a': + case 'A': + case 'b': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (sprnt_fp (&buf, p, spec) < 0) + goto overflow_error; + break; + + default: + /* unsupported specifier */ + goto error; + } + } + else + /* gmp_printf specification, step forward in the va_list */ + { + CONSUME_VA_ARG (spec, ap); + xgmp_fmt_flag = 1; + } + } + + if (start != fmt) + FLUSH (xgmp_fmt_flag, start, fmt, ap2, &buf); + + va_end (ap2); + nbchar = buf.curr - buf.start; + MPFR_ASSERTD (nbchar == strlen (buf.start)); + buf.start = + (char *) (*__gmp_reallocate_func) (buf.start, buf.size, nbchar + 1); + buf.size = nbchar + 1; /* update needed for __gmp_free_func below when + nbchar is too large (overflow_error) */ + *ptr = buf.start; + + /* If nbchar is larger than INT_MAX, the ISO C99 standard is silent, but + POSIX says concerning the snprintf() function: + "[EOVERFLOW] The value of n is greater than {INT_MAX} or the + number of bytes needed to hold the output excluding the + terminating null is greater than {INT_MAX}." See: + http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html + But it doesn't say anything concerning the other printf-like functions. + A defect report has been submitted to austin-review-l (item 2532). + So, for the time being, we return a negative value and set the erange + flag, and set errno to EOVERFLOW in POSIX system. */ + if (nbchar <= INT_MAX) + { + MPFR_SAVE_EXPO_FREE (expo); + return nbchar; + } + + overflow_error: + MPFR_SAVE_EXPO_UPDATE_FLAGS(expo, MPFR_FLAGS_ERANGE); +#ifdef EOVERFLOW + errno = EOVERFLOW; +#endif + + error: + MPFR_SAVE_EXPO_FREE (expo); + *ptr = NULL; + (*__gmp_free_func) (buf.start, buf.size); + + return -1; +} + +#endif /* HAVE_STDARG */ |