]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - mpfr/vasprintf.c
Imported gcc-4.4.3
[msp430-gcc.git] / mpfr / vasprintf.c
diff --git a/mpfr/vasprintf.c b/mpfr/vasprintf.c
new file mode 100644 (file)
index 0000000..c227035
--- /dev/null
@@ -0,0 +1,2117 @@
+/* mpfr_vasprintf -- main function for the printf functions family
+   plus helper macros & functions.
+
+Copyright 2007, 2008, 2009 Free Software Foundation, Inc.
+Contributed by the Arenaire and Cacao 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 2.1 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.LIB.  If not, 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>
+
+#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
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h> /* for intmax_t */
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+
+#include <string.h>             /* for strlen, memcpy and others */
+
+#include "mpfr-impl.h"
+
+/* Define a length modifier corresponding to mp_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
+
+#if (__GMP_MP_SIZE_T_INT == 1)
+#define MPFR_EXP_FORMAT_SPEC "i"
+#elif (__GMP_MP_SIZE_T_INT == 0)
+#define MPFR_EXP_FORMAT_SPEC "li"
+#else
+#error "mp_exp_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,
+    QUAD_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 */
+  mp_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 = GMP_RNDN;
+  specinfo->spec = 'i';
+  specinfo->pad = ' ';
+}
+
+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. */
+#ifdef HAVE_WCHAR_H
+#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), wint_t);                                       \
+  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_ptr);           \
+        break;                                  \
+      case MP_LIMB_ARRAY_ARG:                   \
+        (void) va_arg ((ap), mp_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) * sizeof(char));     \
+        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;                   /* last character (!= '\0') written */
+  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 = sizeof (char) * 4096 * (1 + len / 4096);
+
+  b->start =
+    (char *) (*__gmp_reallocate_func) (b->start, b->size, b->size + n);
+  b->size += n;
+  b->curr = b->start + pos;
+}
+
+/* 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)
+{
+  if (len == 0)
+    return;
+
+  MPFR_ASSERTN (b->size < SIZE_MAX - len - 1);
+  MPFR_ASSERTD (len <= strlen (s));
+  if (MPFR_UNLIKELY ((b->curr + len + 1) > (b->start + b->size)))
+    buffer_widen (b, len);
+
+  strncat (b->curr, s, len);
+  b->curr += len;
+}
+
+/* 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)
+{
+  if (n == 0)
+    return;
+
+  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';
+}
+
+/* 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;
+
+  if (size == 0)
+    return;
+  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';
+}
+
+/* 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 */
+};
+
+/* 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;
+  mp_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.spec == 'b' || spec.prec != 0)
+    /* In order to avoid ambiguity in rounding to even case, we will always
+       output at least one fractional digit in binary mode */
+    {
+      size_t nsd;
+
+      /* Number of significant digits:
+         - if no given precision, let mpfr_get_str determine it;
+         - if a zero precision is specified and if we are in binary mode, then
+         ask for two binary digits, one before decimal point, and one after;
+         - 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 == 0 && spec.spec == 'b') ? 2 : 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
+    /* One hexadecimal digit is sufficient but mpfr_get_str returns at least
+       two digits when the base is a power of two.
+       So, in order to avoid double rounding, we will build our own string. */
+    {
+      mp_limb_t *pm = MPFR_MANT (p);
+      mp_size_t ps;
+      int digit;
+      unsigned int shift;
+      int rnd_away;
+
+      /* rnd_away:
+         1 if round away from zero,
+         0 if round to zero,
+         -1 if not decided yet. */
+      rnd_away =
+        spec.rnd_mode == GMP_RNDD ? MPFR_IS_NEG (p) :
+        spec.rnd_mode == GMP_RNDU ? MPFR_IS_POS (p) :
+        spec.rnd_mode == GMP_RNDZ ? 0 : -1;
+
+      /* exponent for radix-2 with the decimal point after the first
+         hexadecimal digit */
+      MPFR_ASSERTN (MPFR_GET_EXP (p) > MPFR_EMIN_MIN + 3); /* possible
+                                                              overflow */
+      exp = MPFR_GET_EXP (p) - 4;
+
+      /* Determine the radix-16 digit by grouping the 4 first digits. Even
+         if MPFR_PREC (p) < 4, we can read 4 bits in its first limb */
+      shift = BITS_PER_MP_LIMB - 4;
+      ps = (MPFR_PREC (p) - 1) / BITS_PER_MP_LIMB;
+      digit = pm[ps] >> shift;
+
+      if (MPFR_PREC (p) > 4)
+        /* round taking into account bits outside the first 4 ones */
+        {
+          if (rnd_away == -1)
+            /* Round to nearest mode: we have to decide in that particular
+               case if we have to round away from zero or not */
+            {
+              mp_limb_t limb, rb, mask;
+
+              /* compute rounding bit */
+              mask = MPFR_LIMB_ONE << (shift - 1);
+              rb = pm[ps] & mask;
+              if (rb == 0)
+                rnd_away = 0;
+              else
+                {
+                  mask = MPFR_LIMB_MASK (shift - 1);
+                  limb = pm[ps] & mask;
+                  while ((ps > 0) && (limb == 0))
+                    limb = pm[--ps];
+                  if (limb == 0)
+                    /* tie case, round to even */
+                    rnd_away = (digit & 1) ? 1 : 0;
+                  else
+                    rnd_away = 1;
+                }
+            }
+
+          MPFR_ASSERTD (rnd_away >= 0);  /* rounding direction is defined */
+          if (rnd_away)
+            {
+              digit++;
+              if (digit > 15)
+                /* As we want only the first significant digit, we have
+                   to shift one position to the left */
+                {
+                  digit >>= 1;
+                  ++exp;  /* no possible overflow because
+                             exp == EXP(p)-3 */
+                }
+            }
+        }
+
+      MPFR_ASSERTD ((0 <= digit) && (digit <= 15));
+      np->ip_size = 1;
+      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);
+    }
+
+  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 much 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;
+  {
+    mp_exp_unsigned_t x;
+
+    x = SAFE_ABS (mp_exp_unsigned_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_FORMAT_SPEC);
+
+    if (sprintf (str, exp_fmt, 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'.
+
+   return -1 if some field > INT_MAX */
+static int
+regular_eg (struct number_parts *np, mpfr_srcptr p,
+            const struct printf_spec spec)
+{
+  char *str;
+  mp_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;
+  {
+    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_exp 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);
+  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 much 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;
+  {
+    mp_exp_unsigned_t x;
+
+    x = SAFE_ABS (mp_exp_unsigned_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_FORMAT_SPEC);
+
+    if (sprintf (str, exp_fmt, 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'.
+
+   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)
+{
+  mpfr_t x;
+  char * str;
+  const int spec_g = (spec.spec == 'g' || spec.spec == 'G');
+  const int keep_trailing_zeros = spec_g && spec.alt;
+
+  /* sign */
+  if (MPFR_IS_NEG (p))
+    np->sign = '-';
+  else if (spec.showsign || spec.space)
+    np->sign = spec.showsign ? '+' : ' ';
+
+  /* Determine the position of the most significant decimal digit. */
+  {
+    /* Let p = m*10^e with 1 <= m < 10 and p = n*2^d with 0.5 <= d < 1.
+       We need at most 1+log2(floor(d/3)+1) bits of precision in order to
+       represent the exact value of e+1 if p >= 1, or |e| if p < 1. */
+    mp_prec_t m;
+    mp_prec_t n;
+
+    m = (mp_prec_t) SAFE_ABS (mp_exp_unsigned_t, MPFR_GET_EXP (p));
+    m /= 3;
+    m++;
+    n = 1;
+    while (m != 0)
+      {
+        m >>= 1;
+        n++;
+      }
+
+    if (n <= MPFR_PREC (p))
+      mpfr_init2 (x, MPFR_PREC (p) + 1);
+    else
+      mpfr_init2 (x, n);
+  }
+
+  if (MPFR_GET_EXP (p) <= 0)
+    /* 0 < p < 1 */
+    {
+      int rnd_to_one;
+
+      /* Is p round to +/-1 with rounding mode spec.rnd_mode and precision
+         spec.prec ? rnd_to_one:
+         1 if |p| output as "1.00_0"
+         0 if |p| output as "0.dd_d"
+         -1 if not decided yet */
+
+      if (spec_g || spec.prec >= 0)
+        {
+          mpfr_t y;
+          mpfr_t u;
+
+          mpfr_init2 (u, MPFR_PREC (p));
+
+          /* compare y = |p| and 1 - 10^(-spec.prec) */
+          MPFR_ALIAS (y, p, 1, MPFR_EXP (p));
+          mpfr_set_si (u, -spec.prec, GMP_RNDN); /* FIXME: analyze error */
+          mpfr_exp10 (u, u, GMP_RNDN);
+          mpfr_ui_sub (x, 1, u, GMP_RNDN);
+
+          rnd_to_one =
+            mpfr_cmp (y, x) < 0 ? 0 :
+            spec.rnd_mode == GMP_RNDD ? MPFR_IS_NEG (p) :
+            spec.rnd_mode == GMP_RNDU ? MPFR_IS_POS (p) :
+            spec.rnd_mode == GMP_RNDZ ? 0 : -1;
+
+          if (rnd_to_one == -1)
+            /* round to nearest mode */
+            {
+              /* round to 1 iff y = |p| > 1 - 0.5 * 10^(-spec.prec) */
+              mpfr_div_2ui (x, u, 1, GMP_RNDN);
+              mpfr_ui_sub (x, 1, x, GMP_RNDN);
+
+              rnd_to_one = mpfr_cmp (y, x) > 0 ? 1 : 0;
+            }
+          mpfr_clear (u);
+        }
+      else
+        rnd_to_one = 0;
+
+      MPFR_ASSERTD (rnd_to_one >= 0); /* rnd_to_one is defined */
+      if (rnd_to_one)
+        /* one digit '1' in integral part */
+        {
+          /* integral part */
+          np->ip_size = 1;
+          str = (char *) (*__gmp_allocate_func) (1 + np->ip_size);
+          str[0] = '1';
+          str[1] = '\0';
+          np->ip_ptr = register_string (np->sl, str);
+
+          if (spec.prec > 0)
+            /* fractional part */
+            {
+              if (spec_g)
+                /* with specifier 'g', spec.prec is the number of
+                   significant digits to display, take into account the digit
+                   '1' in the integral part*/
+                np->fp_trailing_zeros = spec.alt ? spec.prec - 1 : 0;
+              else
+                /* with specifier 'f', spec.prec is the number of digits
+                   after the decimal point */
+                np->fp_trailing_zeros = spec.prec;
+            }
+        }
+      else
+        /* one digit '0' in integral part */
+        {
+          /* 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)
+            /* fractional part */
+            {
+              mpfr_t y;
+
+              MPFR_ALIAS (y, p, 1, MPFR_EXP (p)); /* y = |p| */
+              mpfr_log10 (x, y, GMP_RNDD); /* FIXME: analyze error */
+              mpfr_floor (x, x);
+              mpfr_abs (x, x, GMP_RNDD);
+              /* We have rounded away from zero so that x == |e| (with
+                 p = m*10^e, see above). */
+
+              if ((spec.prec > 0 && mpfr_cmp_si (x, spec.prec) > 0)
+                  || (spec_g && mpfr_cmp_ui (x, 5) == 0))
+                /* p is too small for the given precision,
+                   output "0.0_00" or "0.0_01" depending on rnd_mode */
+                {
+                  int rnd_away;
+
+                  /* rnd_away:
+                     1 if round away from zero,
+                     0 if round to zero,
+                     -1 if not decided yet. */
+                  rnd_away =
+                    spec.rnd_mode == GMP_RNDD ? MPFR_IS_NEG (p) :
+                    spec.rnd_mode == GMP_RNDU ? MPFR_IS_POS (p) :
+                    spec.rnd_mode == GMP_RNDZ ? 0 : -1;
+
+                  if (rnd_away == -1)
+                    /* round to nearest mode */
+                    {
+                      /* round away iff |p| with x = 0.5 * 10^(-spec.prec) */
+                      mpfr_set_si (x, -spec.prec, GMP_RNDN);
+                      mpfr_exp10 (x, x, GMP_RNDN);
+                      mpfr_div_2ui (x, x, 1, GMP_RNDN);
+
+                      rnd_away = mpfr_cmp (y, x) > 0 ? 1 : 0;
+                    }
+
+                  MPFR_ASSERTD (rnd_away >= 0);  /* rounding direction is
+                                                    defined */
+                  if (rnd_away)
+                    /* the last output digit is '1' */
+                    {
+                      if (spec_g)
+                        /* |p| is output as 0.0001 */
+                        np->fp_leading_zeros = 3;
+                      else
+                        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 spec.prec zeros in fractional part */
+                    np->fp_leading_zeros = spec.prec;
+                }
+              else
+                /* some significant digits can be output in the fractional
+                   part */
+                {
+                  mp_exp_t exp;
+                  char *ptr;
+                  size_t str_len;
+                  const size_t nsd = spec.prec < 0 ? 0
+                    : spec.prec - mpfr_get_ui (x, GMP_RNDZ) + 1;
+                  /* WARNING: nsd may equal 1, we use here 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);
+                  np->fp_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign */
+                  np->fp_leading_zeros = exp < 0 ? -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 much digits in fractional part */
+                    {
+                      mpfr_clear (x);
+                      return -1;
+                    }
+                  MPFR_ASSERTD (str_len > 0);
+                  np->fp_size = str_len;
+
+                  if (!spec_g && (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 */
+    {
+      mp_exp_t exp;
+      size_t nsd;  /* Number of significant digits */
+
+      mpfr_abs (x, p, GMP_RNDD); /* With our choice of precision,
+                                    x == |p| exactly. */
+      mpfr_log10 (x, x, GMP_RNDZ);
+      mpfr_floor (x, x);
+      mpfr_add_ui (x, x, 1, GMP_RNDZ);
+      /* We have rounded towards zero so that x == e + 1 (with p = m*10^e,
+         see above). x is now the number of digits in the integral part. */
+
+      MPFR_ASSERTD (mpfr_cmp_si (x, 0) >= 0);
+      if (mpfr_cmp_ui (x, INT_MAX) > 0)
+        /* P is too large to print all its integral part digits */
+        {
+          mpfr_clear (x);
+          return -1;
+        }
+
+      np->ip_size = mpfr_get_ui (x, GMP_RNDN);
+
+      nsd = spec.prec < 0 ? 0 : spec.prec + np->ip_size;
+      str = mpfr_get_str (NULL, &exp, 10, nsd, p, spec.rnd_mode);
+      register_string (np->sl, str);
+      np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign */
+
+      if (spec.group)
+        /* thousands separator in integral part */
+        np->thousands_sep = MPFR_THOUSANDS_SEPARATOR;
+
+      if (nsd == 0 || (spec_g && !spec.alt))
+        /* compute how much non-zero digits in integral and fractional
+           parts */
+        {
+          size_t str_len;
+          str_len = strlen (str); /* note: the sign has been skipped */
+
+          if (np->ip_size > str_len)
+            /* mpfr_get_str doesn't give the trailing zeros when p is a
+               multiple of 10 (p integer, so no fractional part) */
+            {
+              np->ip_trailing_zeros = np->ip_size - str_len;
+              np->ip_size = str_len;
+              if (spec.alt)
+                np->point = MPFR_DECIMAL_POINT;
+            }
+          else
+            /* str may contain some digits which are in fractional part */
+            {
+              char *ptr;
+
+              ptr = str + str_len - 1; /* points to the end of str */
+              str_len -= np->ip_size;  /* number of digits in fractional
+                                          part */
+
+              if (!keep_trailing_zeros)
+                /* remove trailing zeros, if any */
+                {
+                  while ((*ptr == '0') && (str_len != 0))
+                    {
+                      --ptr;
+                      --str_len;
+                    }
+                }
+
+              if (str_len > INT_MAX)
+                /* too much digits in fractional part */
+                {
+                  mpfr_clear (x);
+                  return -1;
+                }
+
+              if (str_len != 0)
+                /* some digits in fractional part */
+                {
+                  np->point = MPFR_DECIMAL_POINT;
+                  np->fp_ptr = str + np->ip_size;
+                  np->fp_size = str_len;
+                }
+              else if (spec.alt)
+                np->point = MPFR_DECIMAL_POINT;
+            }
+        }
+      else
+        /* spec.prec digits in fractional part */
+        {
+          MPFR_ASSERTD (np->ip_size == exp);
+
+          if (spec.prec != 0)
+            {
+              np->point = MPFR_DECIMAL_POINT;
+              np->fp_ptr = str + np->ip_size;
+              np->fp_size = spec.prec;
+            }
+          else if (spec.alt)
+            np->point = MPFR_DECIMAL_POINT;
+        }
+    }
+
+  mpfr_clear (x);
+  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.prec != 'G') || spec.alt))
+            /* fractional part */
+            {
+              np->point = MPFR_DECIMAL_POINT;
+              np->fp_trailing_zeros = (spec.spec == 'g' && spec.prec == '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 (regular_fg (np, p, spec) == -1)
+            goto error;
+        }
+      else if (spec.spec == 'e' || spec.spec == 'E')
+        {
+          if (regular_eg (np, p, spec) == -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'. */
+          int threshold;
+          long x;
+          mpfr_t y;
+
+          MPFR_ALIAS (y, p, 1, MPFR_EXP (p)); /* y = |p| */
+
+          threshold = (spec.prec < 0) ? 6 : (spec.prec == 0) ? 1 : spec.prec;
+          {
+            mpfr_t z;
+
+            mpfr_init2 (z, 53);
+            mpfr_log10 (z, y, GMP_RNDD);
+            x = mpfr_get_si (z, GMP_RNDD);
+            mpfr_clear (z);
+          }
+
+          if (x < threshold && x >= -5)
+            {
+              if (x == -5)
+                /* |p| might be rounded to 1e-4 */
+                {
+                  int round_to_1em4;
+
+                  /* round_to_1em4:
+                     1 if |p| rounded to 1e-4,
+                     0 if not,
+                     -1 if not decided yet. */
+                  round_to_1em4 =
+                    spec.rnd_mode == GMP_RNDD ? MPFR_IS_NEG (p) :
+                    spec.rnd_mode == GMP_RNDU ? MPFR_IS_POS (p) :
+                    spec.rnd_mode == GMP_RNDZ ? 0 : -1;
+
+                  if (round_to_1em4 == -1)
+                    /* round to nearest mode: |p| is output as "1e-04" iff
+                       0 < 10^(-4) - |p| <= 5 * 10^(-threshold-5) */
+                    {
+                      mpfr_t z;
+
+                      mpfr_init2 (z, MPFR_PREC (p)); /* FIXME: analyse error*/
+                      mpfr_set_si (z, -threshold, GMP_RNDN);
+                      mpfr_exp10 (z, z, GMP_RNDN);
+                      mpfr_div_2ui (z, z, 1, GMP_RNDN);
+                      mpfr_ui_sub (z, 1, z, GMP_RNDN);
+                      /* here, z = 1 - 10^(-threshold)/2 */
+
+                      mpfr_div_ui (z, z, 625, GMP_RNDN);
+                      mpfr_div_2ui (z, z, 4, GMP_RNDN);
+
+                      round_to_1em4 = mpfr_cmp (y, z) < 0 ? 0 : 1;
+                      mpfr_clear (z);
+                    }
+
+                  MPFR_ASSERTD (round_to_1em4 >= 0); /* rounding is defined */
+                  if (round_to_1em4)
+                    /* |p| = 0.0000abc_d is output as "1.00_0e-04" with
+                       style 'e', so the conversion is with style 'f' */
+                    {
+                      spec.prec = threshold + 3;
+
+                      if (regular_fg (np, p, spec) == -1)
+                        goto error;
+                    }
+                  else
+                    /* |p| = 0.0000abc_d is output as "a.bc_de-05" with
+                       style 'e', so the conversion is with style 'e' */
+                    {
+                      spec.prec = threshold - 1;
+
+                      if (regular_eg (np, p, spec) == -1)
+                        goto error;
+                    }
+                }
+              else
+                /* x >= -4, the conversion is with style 'f' */
+                {
+                  spec.prec = threshold - 1 - x;
+
+                  if (regular_fg (np, p, spec) == -1)
+                    goto error;
+                }
+            }
+          else
+            {
+              spec.prec = threshold - 1;
+
+              if (regular_eg (np, p, spec) == -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 gmp_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 * sizeof (char));
+  gmp_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;
+          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 = GMP_RNDD;
+              break;
+            case 'U':
+              ++fmt;
+              spec.rnd_mode = GMP_RNDU;
+              break;
+            case 'Z':
+              ++fmt;
+              spec.rnd_mode = GMP_RNDZ;
+              break;
+            case 'N':
+              ++fmt;
+            default:
+              spec.rnd_mode = GMP_RNDN;
+            }
+        }
+
+      spec.spec = *fmt;
+      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 (gmp_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 mp_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 (gmp_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 (gmp_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);
+          gmp_fmt_flag = 1;
+        }
+    }
+
+  if (start != fmt)
+    FLUSH (gmp_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);
+  *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 */