xref: /dflybsd-src/contrib/mpfr/src/vasprintf.c (revision 122b686e384c0fb6cfc3dd8cf3a16b87651fc609)
14a238c70SJohn Marino /* mpfr_vasprintf -- main function for the printf functions family
24a238c70SJohn Marino    plus helper macros & functions.
34a238c70SJohn Marino 
4ab6d115fSJohn Marino Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
5ab6d115fSJohn Marino Contributed by the AriC and Caramel projects, INRIA.
64a238c70SJohn Marino 
74a238c70SJohn Marino This file is part of the GNU MPFR Library.
84a238c70SJohn Marino 
94a238c70SJohn Marino The GNU MPFR Library is free software; you can redistribute it and/or modify
104a238c70SJohn Marino it under the terms of the GNU Lesser General Public License as published by
114a238c70SJohn Marino the Free Software Foundation; either version 3 of the License, or (at your
124a238c70SJohn Marino option) any later version.
134a238c70SJohn Marino 
144a238c70SJohn Marino The GNU MPFR Library is distributed in the hope that it will be useful, but
154a238c70SJohn Marino WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
164a238c70SJohn Marino or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
174a238c70SJohn Marino License for more details.
184a238c70SJohn Marino 
194a238c70SJohn Marino You should have received a copy of the GNU Lesser General Public License
204a238c70SJohn Marino along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
214a238c70SJohn Marino http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
224a238c70SJohn Marino 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
234a238c70SJohn Marino 
244a238c70SJohn Marino #ifdef HAVE_CONFIG_H
254a238c70SJohn Marino #include "config.h"
264a238c70SJohn Marino #endif
274a238c70SJohn Marino 
284a238c70SJohn Marino /* The mpfr_printf-like functions are defined only if <stdarg.h> exists */
294a238c70SJohn Marino #ifdef HAVE_STDARG
304a238c70SJohn Marino 
314a238c70SJohn Marino #include <stdarg.h>
324a238c70SJohn Marino 
334a238c70SJohn Marino #ifndef HAVE_VA_COPY
344a238c70SJohn Marino # ifdef HAVE___VA_COPY
354a238c70SJohn Marino #  define va_copy(dst,src) __va_copy(dst, src)
364a238c70SJohn Marino # else
374a238c70SJohn Marino /* autoconf manual advocates this fallback.
384a238c70SJohn Marino    This is also the solution chosen by gmp */
394a238c70SJohn Marino #  define va_copy(dst,src) \
404a238c70SJohn Marino   do { memcpy(&(dst), &(src), sizeof(va_list)); } while (0)
414a238c70SJohn Marino # endif /* HAVE___VA_COPY */
424a238c70SJohn Marino #endif /* HAVE_VA_COPY */
434a238c70SJohn Marino 
444a238c70SJohn Marino #ifdef HAVE_WCHAR_H
454a238c70SJohn Marino #include <wchar.h>
464a238c70SJohn Marino #endif
474a238c70SJohn Marino 
484a238c70SJohn Marino #if defined (__cplusplus)
494a238c70SJohn Marino #include <cstddef>
504a238c70SJohn Marino #define __STDC_LIMIT_MACROS   /* SIZE_MAX defined with <stdint.h> inclusion */
514a238c70SJohn Marino #else
524a238c70SJohn Marino #include <stddef.h>             /* for ptrdiff_t */
534a238c70SJohn Marino #endif
544a238c70SJohn Marino 
554a238c70SJohn Marino #define MPFR_NEED_LONGLONG_H
564a238c70SJohn Marino #include "mpfr-intmax.h"
574a238c70SJohn Marino #include "mpfr-impl.h"
584a238c70SJohn Marino 
594a238c70SJohn Marino /* Define a length modifier corresponding to mpfr_prec_t.
604a238c70SJohn Marino    We use literal string instead of literal character so as to permit future
614a238c70SJohn Marino    extension to long long int ("ll"). */
624a238c70SJohn Marino #if   _MPFR_PREC_FORMAT == 1
634a238c70SJohn Marino #define MPFR_PREC_FORMAT_TYPE "h"
644a238c70SJohn Marino #define MPFR_PREC_FORMAT_SIZE 1
654a238c70SJohn Marino #elif _MPFR_PREC_FORMAT == 2
664a238c70SJohn Marino #define MPFR_PREC_FORMAT_TYPE ""
674a238c70SJohn Marino #define MPFR_PREC_FORMAT_SIZE 0
684a238c70SJohn Marino #elif _MPFR_PREC_FORMAT == 3
694a238c70SJohn Marino #define MPFR_PREC_FORMAT_TYPE "l"
704a238c70SJohn Marino #define MPFR_PREC_FORMAT_SIZE 1
714a238c70SJohn Marino #else
724a238c70SJohn Marino #error "mpfr_prec_t size not supported"
734a238c70SJohn Marino #endif
744a238c70SJohn Marino 
754a238c70SJohn Marino /* Output for special values defined in the C99 standard */
764a238c70SJohn Marino #define MPFR_NAN_STRING_LC "nan"
774a238c70SJohn Marino #define MPFR_NAN_STRING_UC "NAN"
784a238c70SJohn Marino #define MPFR_NAN_STRING_LENGTH 3
794a238c70SJohn Marino #define MPFR_INF_STRING_LC "inf"
804a238c70SJohn Marino #define MPFR_INF_STRING_UC "INF"
814a238c70SJohn Marino #define MPFR_INF_STRING_LENGTH 3
824a238c70SJohn Marino 
834a238c70SJohn Marino /* The implicit \0 is useless, but we do not write num_to_text[16]
844a238c70SJohn Marino    otherwise g++ complains. */
854a238c70SJohn Marino static const char num_to_text[] = "0123456789abcdef";
864a238c70SJohn Marino 
874a238c70SJohn Marino /* some macro and functions for parsing format string */
884a238c70SJohn Marino 
894a238c70SJohn Marino /* Read an integer; saturate to INT_MAX. */
904a238c70SJohn Marino #define READ_INT(ap, format, specinfo, field, label_out)                \
914a238c70SJohn Marino   do {                                                                  \
924a238c70SJohn Marino     while (*(format))                                                   \
934a238c70SJohn Marino       {                                                                 \
944a238c70SJohn Marino         int _i;                                                         \
954a238c70SJohn Marino         switch (*(format))                                              \
964a238c70SJohn Marino           {                                                             \
974a238c70SJohn Marino           case '0':                                                     \
984a238c70SJohn Marino           case '1':                                                     \
994a238c70SJohn Marino           case '2':                                                     \
1004a238c70SJohn Marino           case '3':                                                     \
1014a238c70SJohn Marino           case '4':                                                     \
1024a238c70SJohn Marino           case '5':                                                     \
1034a238c70SJohn Marino           case '6':                                                     \
1044a238c70SJohn Marino           case '7':                                                     \
1054a238c70SJohn Marino           case '8':                                                     \
1064a238c70SJohn Marino           case '9':                                                     \
1074a238c70SJohn Marino             specinfo.field = (specinfo.field <= INT_MAX / 10) ?         \
1084a238c70SJohn Marino               specinfo.field * 10 : INT_MAX;                            \
1094a238c70SJohn Marino             _i = *(format) - '0';                                       \
1104a238c70SJohn Marino             MPFR_ASSERTN (_i >= 0 && _i <= 9);                          \
1114a238c70SJohn Marino             specinfo.field = (specinfo.field <= INT_MAX - _i) ?         \
1124a238c70SJohn Marino               specinfo.field + _i : INT_MAX;                            \
1134a238c70SJohn Marino             ++(format);                                                 \
1144a238c70SJohn Marino             break;                                                      \
1154a238c70SJohn Marino           case '*':                                                     \
1164a238c70SJohn Marino             specinfo.field = va_arg ((ap), int);                        \
1174a238c70SJohn Marino             ++(format);                                                 \
1184a238c70SJohn Marino           default:                                                      \
1194a238c70SJohn Marino             goto label_out;                                             \
1204a238c70SJohn Marino           }                                                             \
1214a238c70SJohn Marino       }                                                                 \
1224a238c70SJohn Marino   } while (0)
1234a238c70SJohn Marino 
1244a238c70SJohn Marino /* arg_t contains all the types described by the 'type' field of the
1254a238c70SJohn Marino    format string */
1264a238c70SJohn Marino enum arg_t
1274a238c70SJohn Marino   {
1284a238c70SJohn Marino     NONE,
1294a238c70SJohn Marino     CHAR_ARG,
1304a238c70SJohn Marino     SHORT_ARG,
1314a238c70SJohn Marino     LONG_ARG,
1324a238c70SJohn Marino     LONG_LONG_ARG,
1334a238c70SJohn Marino     INTMAX_ARG,
1344a238c70SJohn Marino     SIZE_ARG,
1354a238c70SJohn Marino     PTRDIFF_ARG,
1364a238c70SJohn Marino     LONG_DOUBLE_ARG,
1374a238c70SJohn Marino     MPF_ARG,
1384a238c70SJohn Marino     MPQ_ARG,
1394a238c70SJohn Marino     MP_LIMB_ARG,
1404a238c70SJohn Marino     MP_LIMB_ARRAY_ARG,
1414a238c70SJohn Marino     MPZ_ARG,
1424a238c70SJohn Marino     MPFR_PREC_ARG,
1434a238c70SJohn Marino     MPFR_ARG,
1444a238c70SJohn Marino     UNSUPPORTED
1454a238c70SJohn Marino   };
1464a238c70SJohn Marino 
1474a238c70SJohn Marino /* Each conversion specification of the format string will be translated in a
1484a238c70SJohn Marino    printf_spec structure by the parser.
1494a238c70SJohn Marino    This structure is adapted from the GNU libc one. */
1504a238c70SJohn Marino struct printf_spec
1514a238c70SJohn Marino {
1524a238c70SJohn Marino   unsigned int alt:1;           /* # flag */
1534a238c70SJohn Marino   unsigned int space:1;         /* Space flag */
1544a238c70SJohn Marino   unsigned int left:1;          /* - flag */
1554a238c70SJohn Marino   unsigned int showsign:1;      /* + flag */
1564a238c70SJohn Marino   unsigned int group:1;         /* ' flag */
1574a238c70SJohn Marino 
1584a238c70SJohn Marino   int width;                    /* Width */
1594a238c70SJohn Marino   int prec;                     /* Precision */
1604a238c70SJohn Marino 
1614a238c70SJohn Marino   enum arg_t arg_type;          /* Type of argument */
1624a238c70SJohn Marino   mpfr_rnd_t rnd_mode;            /* Rounding mode */
1634a238c70SJohn Marino   char spec;                    /* Conversion specifier */
1644a238c70SJohn Marino 
1654a238c70SJohn Marino   char pad;                     /* Padding character */
1664a238c70SJohn Marino };
1674a238c70SJohn Marino 
1684a238c70SJohn Marino static void
specinfo_init(struct printf_spec * specinfo)1694a238c70SJohn Marino specinfo_init (struct printf_spec *specinfo)
1704a238c70SJohn Marino {
1714a238c70SJohn Marino   specinfo->alt = 0;
1724a238c70SJohn Marino   specinfo->space = 0;
1734a238c70SJohn Marino   specinfo->left = 0;
1744a238c70SJohn Marino   specinfo->showsign = 0;
1754a238c70SJohn Marino   specinfo->group = 0;
1764a238c70SJohn Marino   specinfo->width = 0;
1774a238c70SJohn Marino   specinfo->prec = 0;
1784a238c70SJohn Marino   specinfo->arg_type = NONE;
1794a238c70SJohn Marino   specinfo->rnd_mode = MPFR_RNDN;
1804a238c70SJohn Marino   specinfo->spec = '\0';
1814a238c70SJohn Marino   specinfo->pad = ' ';
1824a238c70SJohn Marino }
1834a238c70SJohn Marino 
1844a238c70SJohn Marino #define FLOATING_POINT_ARG_TYPE(at) \
1854a238c70SJohn Marino   ((at) == MPFR_ARG || (at) == MPF_ARG || (at) == LONG_DOUBLE_ARG)
1864a238c70SJohn Marino 
1874a238c70SJohn Marino #define INTEGER_LIKE_ARG_TYPE(at)                                       \
1884a238c70SJohn Marino   ((at) == SHORT_ARG || (at) == LONG_ARG || (at) == LONG_LONG_ARG       \
1894a238c70SJohn Marino    || (at) == INTMAX_ARG  || (at) == MPFR_PREC_ARG || (at) == MPZ_ARG   \
1904a238c70SJohn Marino    || (at) == MPQ_ARG || (at) == MP_LIMB_ARG || (at) == MP_LIMB_ARRAY_ARG \
1914a238c70SJohn Marino    || (at) == CHAR_ARG || (at) == SIZE_ARG || (at) == PTRDIFF_ARG)
1924a238c70SJohn Marino 
1934a238c70SJohn Marino static int
specinfo_is_valid(struct printf_spec spec)1944a238c70SJohn Marino specinfo_is_valid (struct printf_spec spec)
1954a238c70SJohn Marino {
1964a238c70SJohn Marino   switch (spec.spec)
1974a238c70SJohn Marino     {
1984a238c70SJohn Marino     case 'n':
1994a238c70SJohn Marino       return -1;
2004a238c70SJohn Marino 
2014a238c70SJohn Marino     case 'a':    case 'A':
2024a238c70SJohn Marino     case 'e':    case 'E':
2034a238c70SJohn Marino     case 'f':    case 'F':
2044a238c70SJohn Marino     case 'g':    case 'G':
2054a238c70SJohn Marino       return (spec.arg_type == NONE
2064a238c70SJohn Marino               || FLOATING_POINT_ARG_TYPE (spec.arg_type));
2074a238c70SJohn Marino 
2084a238c70SJohn Marino     case 'b':
2094a238c70SJohn Marino       return spec.arg_type == MPFR_ARG;
2104a238c70SJohn Marino 
2114a238c70SJohn Marino     case 'd':    case 'i':
2124a238c70SJohn Marino     case 'u':    case 'o':
2134a238c70SJohn Marino     case 'x':    case 'X':
2144a238c70SJohn Marino       return (spec.arg_type == NONE
2154a238c70SJohn Marino               || INTEGER_LIKE_ARG_TYPE (spec.arg_type));
2164a238c70SJohn Marino 
2174a238c70SJohn Marino     case 'c':
2184a238c70SJohn Marino     case 's':
2194a238c70SJohn Marino       return (spec.arg_type == NONE || spec.arg_type == LONG_ARG);
2204a238c70SJohn Marino 
2214a238c70SJohn Marino     case 'p':
2224a238c70SJohn Marino       return spec.arg_type == NONE;
2234a238c70SJohn Marino 
2244a238c70SJohn Marino     default:
2254a238c70SJohn Marino       return 0;
2264a238c70SJohn Marino     }
2274a238c70SJohn Marino }
2284a238c70SJohn Marino 
2294a238c70SJohn Marino static const char *
parse_flags(const char * format,struct printf_spec * specinfo)2304a238c70SJohn Marino parse_flags (const char *format, struct printf_spec *specinfo)
2314a238c70SJohn Marino {
2324a238c70SJohn Marino   while (*format)
2334a238c70SJohn Marino     {
2344a238c70SJohn Marino       switch (*format)
2354a238c70SJohn Marino         {
2364a238c70SJohn Marino         case '0':
2374a238c70SJohn Marino           specinfo->pad = '0';
2384a238c70SJohn Marino           ++format;
2394a238c70SJohn Marino           break;
2404a238c70SJohn Marino         case '#':
2414a238c70SJohn Marino           specinfo->alt = 1;
2424a238c70SJohn Marino           ++format;
2434a238c70SJohn Marino           break;
2444a238c70SJohn Marino         case '+':
2454a238c70SJohn Marino           specinfo->showsign = 1;
2464a238c70SJohn Marino           ++format;
2474a238c70SJohn Marino           break;
2484a238c70SJohn Marino         case ' ':
2494a238c70SJohn Marino           specinfo->space = 1;
2504a238c70SJohn Marino           ++format;
2514a238c70SJohn Marino           break;
2524a238c70SJohn Marino         case '-':
2534a238c70SJohn Marino           specinfo->left = 1;
2544a238c70SJohn Marino           ++format;
2554a238c70SJohn Marino           break;
2564a238c70SJohn Marino         case '\'':
2574a238c70SJohn Marino           /* Single UNIX Specification for thousand separator */
2584a238c70SJohn Marino           specinfo->group = 1;
2594a238c70SJohn Marino           ++format;
2604a238c70SJohn Marino           break;
2614a238c70SJohn Marino         default:
2624a238c70SJohn Marino           return format;
2634a238c70SJohn Marino         }
2644a238c70SJohn Marino     }
2654a238c70SJohn Marino   return format;
2664a238c70SJohn Marino }
2674a238c70SJohn Marino 
2684a238c70SJohn Marino static const char *
parse_arg_type(const char * format,struct printf_spec * specinfo)2694a238c70SJohn Marino parse_arg_type (const char *format, struct printf_spec *specinfo)
2704a238c70SJohn Marino {
2714a238c70SJohn Marino   switch (*format)
2724a238c70SJohn Marino     {
2734a238c70SJohn Marino     case '\0':
2744a238c70SJohn Marino       break;
2754a238c70SJohn Marino     case 'h':
2764a238c70SJohn Marino       if (*++format == 'h')
2774a238c70SJohn Marino #ifndef NPRINTF_HH
2784a238c70SJohn Marino         {
2794a238c70SJohn Marino           ++format;
2804a238c70SJohn Marino           specinfo->arg_type = CHAR_ARG;
2814a238c70SJohn Marino         }
2824a238c70SJohn Marino #else
2834a238c70SJohn Marino         specinfo->arg_type = UNSUPPORTED;
2844a238c70SJohn Marino #endif
2854a238c70SJohn Marino       else
2864a238c70SJohn Marino         specinfo->arg_type = SHORT_ARG;
2874a238c70SJohn Marino       break;
2884a238c70SJohn Marino     case 'l':
2894a238c70SJohn Marino       if (*++format == 'l')
2904a238c70SJohn Marino         {
2914a238c70SJohn Marino           ++format;
2924a238c70SJohn Marino #if defined (HAVE_LONG_LONG) && !defined(NPRINTF_LL)
2934a238c70SJohn Marino           specinfo->arg_type = LONG_LONG_ARG;
2944a238c70SJohn Marino #else
2954a238c70SJohn Marino           specinfo->arg_type = UNSUPPORTED;
2964a238c70SJohn Marino #endif
2974a238c70SJohn Marino           break;
2984a238c70SJohn Marino         }
2994a238c70SJohn Marino       else
3004a238c70SJohn Marino         {
3014a238c70SJohn Marino           specinfo->arg_type = LONG_ARG;
3024a238c70SJohn Marino           break;
3034a238c70SJohn Marino         }
3044a238c70SJohn Marino     case 'j':
3054a238c70SJohn Marino       ++format;
3064a238c70SJohn Marino #if defined(_MPFR_H_HAVE_INTMAX_T) && !defined(NPRINTF_J)
3074a238c70SJohn Marino       specinfo->arg_type = INTMAX_ARG;
3084a238c70SJohn Marino #else
3094a238c70SJohn Marino       specinfo->arg_type = UNSUPPORTED;
3104a238c70SJohn Marino #endif
3114a238c70SJohn Marino       break;
3124a238c70SJohn Marino     case 'z':
3134a238c70SJohn Marino       ++format;
3144a238c70SJohn Marino       specinfo->arg_type = SIZE_ARG;
3154a238c70SJohn Marino       break;
3164a238c70SJohn Marino     case 't':
3174a238c70SJohn Marino       ++format;
3184a238c70SJohn Marino #ifndef NPRINTF_T
3194a238c70SJohn Marino       specinfo->arg_type = PTRDIFF_ARG;
3204a238c70SJohn Marino #else
3214a238c70SJohn Marino       specinfo->arg_type = UNSUPPORTED;
3224a238c70SJohn Marino #endif
3234a238c70SJohn Marino       break;
3244a238c70SJohn Marino     case 'L':
3254a238c70SJohn Marino       ++format;
3264a238c70SJohn Marino #ifndef NPRINTF_L
3274a238c70SJohn Marino       specinfo->arg_type = LONG_DOUBLE_ARG;
3284a238c70SJohn Marino #else
3294a238c70SJohn Marino       specinfo->arg_type = UNSUPPORTED;
3304a238c70SJohn Marino #endif
3314a238c70SJohn Marino       break;
3324a238c70SJohn Marino     case 'F':
3334a238c70SJohn Marino       ++format;
3344a238c70SJohn Marino       specinfo->arg_type = MPF_ARG;
3354a238c70SJohn Marino       break;
3364a238c70SJohn Marino     case 'Q':
3374a238c70SJohn Marino       ++format;
3384a238c70SJohn Marino       specinfo->arg_type = MPQ_ARG;
3394a238c70SJohn Marino       break;
3404a238c70SJohn Marino     case 'M':
3414a238c70SJohn Marino       ++format;
3424a238c70SJohn Marino       /* The 'M' specifier was added in gmp 4.2.0 */
3434a238c70SJohn Marino       specinfo->arg_type = MP_LIMB_ARG;
3444a238c70SJohn Marino       break;
3454a238c70SJohn Marino     case 'N':
3464a238c70SJohn Marino       ++format;
3474a238c70SJohn Marino       specinfo->arg_type = MP_LIMB_ARRAY_ARG;
3484a238c70SJohn Marino       break;
3494a238c70SJohn Marino     case 'Z':
3504a238c70SJohn Marino       ++format;
3514a238c70SJohn Marino       specinfo->arg_type = MPZ_ARG;
3524a238c70SJohn Marino       break;
3534a238c70SJohn Marino 
3544a238c70SJohn Marino       /* mpfr specific specifiers */
3554a238c70SJohn Marino     case 'P':
3564a238c70SJohn Marino       ++format;
3574a238c70SJohn Marino       specinfo->arg_type = MPFR_PREC_ARG;
3584a238c70SJohn Marino       break;
3594a238c70SJohn Marino     case 'R':
3604a238c70SJohn Marino       ++format;
3614a238c70SJohn Marino       specinfo->arg_type = MPFR_ARG;
3624a238c70SJohn Marino     }
3634a238c70SJohn Marino   return format;
3644a238c70SJohn Marino }
3654a238c70SJohn Marino 
3664a238c70SJohn Marino 
3674a238c70SJohn Marino /* some macros and functions filling the buffer */
3684a238c70SJohn Marino 
3694a238c70SJohn Marino /* CONSUME_VA_ARG removes from va_list AP the type expected by SPECINFO */
3704a238c70SJohn Marino 
3714a238c70SJohn Marino /* With a C++ compiler wchar_t and enumeration in va_list are converted to
3724a238c70SJohn Marino    integer type : int, unsigned int, long or unsigned long (unfortunately,
3734a238c70SJohn Marino    this is implementation dependant).
3744a238c70SJohn Marino    We follow gmp which assumes in print/doprnt.c that wchar_t is converted
3754a238c70SJohn Marino    to int (because wchar_t <= int).
3764a238c70SJohn Marino    For wint_t, we assume that the case WINT_MAX < INT_MAX yields an
3774a238c70SJohn Marino    integer promotion. */
3784a238c70SJohn Marino #ifdef HAVE_WCHAR_H
3794a238c70SJohn Marino #if defined(WINT_MAX) && WINT_MAX < INT_MAX
3804a238c70SJohn Marino typedef int    mpfr_va_wint;  /* integer promotion */
3814a238c70SJohn Marino #else
3824a238c70SJohn Marino typedef wint_t mpfr_va_wint;
3834a238c70SJohn Marino #endif
3844a238c70SJohn Marino #define CASE_LONG_ARG(specinfo, ap)                                     \
3854a238c70SJohn Marino   case LONG_ARG:                                                        \
3864a238c70SJohn Marino   if (((specinfo).spec == 'd') || ((specinfo).spec == 'i')              \
3874a238c70SJohn Marino       || ((specinfo).spec == 'o') || ((specinfo).spec == 'u')           \
3884a238c70SJohn Marino       || ((specinfo).spec == 'x') || ((specinfo).spec == 'X'))          \
3894a238c70SJohn Marino     (void) va_arg ((ap), long);                                         \
3904a238c70SJohn Marino   else if ((specinfo).spec == 'c')                                      \
3914a238c70SJohn Marino     (void) va_arg ((ap), mpfr_va_wint);                                 \
3924a238c70SJohn Marino   else if ((specinfo).spec == 's')                                      \
3934a238c70SJohn Marino     (void) va_arg ((ap), int); /* we assume integer promotion */        \
3944a238c70SJohn Marino   break;
3954a238c70SJohn Marino #else
3964a238c70SJohn Marino #define CASE_LONG_ARG(specinfo, ap)             \
3974a238c70SJohn Marino   case LONG_ARG:                                \
3984a238c70SJohn Marino   (void) va_arg ((ap), long);                   \
3994a238c70SJohn Marino   break;
4004a238c70SJohn Marino #endif
4014a238c70SJohn Marino 
4024a238c70SJohn Marino #if defined(_MPFR_H_HAVE_INTMAX_T)
4034a238c70SJohn Marino #define CASE_INTMAX_ARG(specinfo, ap)           \
4044a238c70SJohn Marino   case INTMAX_ARG:                              \
4054a238c70SJohn Marino   (void) va_arg ((ap), intmax_t);               \
4064a238c70SJohn Marino   break;
4074a238c70SJohn Marino #else
4084a238c70SJohn Marino #define CASE_INTMAX_ARG(specinfo, ap)
4094a238c70SJohn Marino #endif
4104a238c70SJohn Marino 
4114a238c70SJohn Marino #ifdef HAVE_LONG_LONG
4124a238c70SJohn Marino #define CASE_LONG_LONG_ARG(specinfo, ap)        \
4134a238c70SJohn Marino   case LONG_LONG_ARG:                           \
4144a238c70SJohn Marino   (void) va_arg ((ap), long long);              \
4154a238c70SJohn Marino   break;
4164a238c70SJohn Marino #else
4174a238c70SJohn Marino #define CASE_LONG_LONG_ARG(specinfo, ap)
4184a238c70SJohn Marino #endif
4194a238c70SJohn Marino 
4204a238c70SJohn Marino #define CONSUME_VA_ARG(specinfo, ap)            \
4214a238c70SJohn Marino   do {                                          \
4224a238c70SJohn Marino     switch ((specinfo).arg_type)                \
4234a238c70SJohn Marino       {                                         \
4244a238c70SJohn Marino       case CHAR_ARG:                            \
4254a238c70SJohn Marino       case SHORT_ARG:                           \
4264a238c70SJohn Marino         (void) va_arg ((ap), int);              \
4274a238c70SJohn Marino         break;                                  \
4284a238c70SJohn Marino       CASE_LONG_ARG (specinfo, ap)              \
4294a238c70SJohn Marino       CASE_LONG_LONG_ARG (specinfo, ap)         \
4304a238c70SJohn Marino       CASE_INTMAX_ARG (specinfo, ap)            \
4314a238c70SJohn Marino       case SIZE_ARG:                            \
4324a238c70SJohn Marino         (void) va_arg ((ap), size_t);           \
4334a238c70SJohn Marino         break;                                  \
4344a238c70SJohn Marino       case PTRDIFF_ARG:                         \
4354a238c70SJohn Marino         (void) va_arg ((ap), ptrdiff_t);        \
4364a238c70SJohn Marino         break;                                  \
4374a238c70SJohn Marino       case LONG_DOUBLE_ARG:                     \
4384a238c70SJohn Marino         (void) va_arg ((ap), long double);      \
4394a238c70SJohn Marino         break;                                  \
4404a238c70SJohn Marino       case MPF_ARG:                             \
4414a238c70SJohn Marino         (void) va_arg ((ap), mpf_srcptr);       \
4424a238c70SJohn Marino         break;                                  \
4434a238c70SJohn Marino       case MPQ_ARG:                             \
4444a238c70SJohn Marino         (void) va_arg ((ap), mpq_srcptr);       \
4454a238c70SJohn Marino         break;                                  \
4464a238c70SJohn Marino       case MP_LIMB_ARG:                         \
4474a238c70SJohn Marino         (void) va_arg ((ap), mp_limb_t);        \
4484a238c70SJohn Marino         break;                                  \
4494a238c70SJohn Marino       case MP_LIMB_ARRAY_ARG:                   \
4504a238c70SJohn Marino         (void) va_arg ((ap), mpfr_limb_ptr);    \
4514a238c70SJohn Marino         (void) va_arg ((ap), mp_size_t);        \
4524a238c70SJohn Marino         break;                                  \
4534a238c70SJohn Marino       case MPZ_ARG:                             \
4544a238c70SJohn Marino         (void) va_arg ((ap), mpz_srcptr);       \
4554a238c70SJohn Marino         break;                                  \
4564a238c70SJohn Marino       default:                                  \
4574a238c70SJohn Marino         switch ((specinfo).spec)                \
4584a238c70SJohn Marino           {                                     \
4594a238c70SJohn Marino           case 'd':                             \
4604a238c70SJohn Marino           case 'i':                             \
4614a238c70SJohn Marino           case 'o':                             \
4624a238c70SJohn Marino           case 'u':                             \
4634a238c70SJohn Marino           case 'x':                             \
4644a238c70SJohn Marino           case 'X':                             \
4654a238c70SJohn Marino           case 'c':                             \
4664a238c70SJohn Marino             (void) va_arg ((ap), int);          \
4674a238c70SJohn Marino             break;                              \
4684a238c70SJohn Marino           case 'f':                             \
4694a238c70SJohn Marino           case 'F':                             \
4704a238c70SJohn Marino           case 'e':                             \
4714a238c70SJohn Marino           case 'E':                             \
4724a238c70SJohn Marino           case 'g':                             \
4734a238c70SJohn Marino           case 'G':                             \
4744a238c70SJohn Marino           case 'a':                             \
4754a238c70SJohn Marino           case 'A':                             \
4764a238c70SJohn Marino             (void) va_arg ((ap), double);       \
4774a238c70SJohn Marino             break;                              \
4784a238c70SJohn Marino           case 's':                             \
4794a238c70SJohn Marino             (void) va_arg ((ap), char *);       \
4804a238c70SJohn Marino             break;                              \
4814a238c70SJohn Marino           case 'p':                             \
4824a238c70SJohn Marino             (void) va_arg ((ap), void *);       \
4834a238c70SJohn Marino           }                                     \
4844a238c70SJohn Marino       }                                         \
4854a238c70SJohn Marino   } while (0)
4864a238c70SJohn Marino 
4874a238c70SJohn Marino /* process the format part which does not deal with mpfr types,
4884a238c70SJohn Marino    jump to external label 'error' if gmp_asprintf return -1. */
4894a238c70SJohn Marino #define FLUSH(flag, start, end, ap, buf_ptr)                            \
4904a238c70SJohn Marino   do {                                                                  \
4914a238c70SJohn Marino     const size_t n = (end) - (start);                                   \
4924a238c70SJohn Marino     if ((flag))                                                         \
4934a238c70SJohn Marino       /* previous specifiers are understood by gmp_printf */            \
4944a238c70SJohn Marino       {                                                                 \
4954a238c70SJohn Marino         MPFR_TMP_DECL (marker);                                         \
4964a238c70SJohn Marino         char *fmt_copy;                                                 \
4974a238c70SJohn Marino         MPFR_TMP_MARK (marker);                                         \
4984a238c70SJohn Marino         fmt_copy = (char*) MPFR_TMP_ALLOC (n + 1);                      \
4994a238c70SJohn Marino         strncpy (fmt_copy, (start), n);                                 \
5004a238c70SJohn Marino         fmt_copy[n] = '\0';                                             \
5014a238c70SJohn Marino         if (sprntf_gmp ((buf_ptr), (fmt_copy), (ap)) == -1)             \
5024a238c70SJohn Marino           {                                                             \
5034a238c70SJohn Marino             MPFR_TMP_FREE (marker);                                     \
5044a238c70SJohn Marino             goto error;                                                 \
5054a238c70SJohn Marino           }                                                             \
5064a238c70SJohn Marino         (flag) = 0;                                                     \
5074a238c70SJohn Marino         MPFR_TMP_FREE (marker);                                         \
5084a238c70SJohn Marino       }                                                                 \
5094a238c70SJohn Marino     else if ((start) != (end))                                          \
5104a238c70SJohn Marino       /* no conversion specification, just simple characters */         \
5114a238c70SJohn Marino       buffer_cat ((buf_ptr), (start), n);                               \
5124a238c70SJohn Marino   } while (0)
5134a238c70SJohn Marino 
5144a238c70SJohn Marino struct string_buffer
5154a238c70SJohn Marino {
5164a238c70SJohn Marino   char *start;                  /* beginning of the buffer */
5174a238c70SJohn Marino   char *curr;                   /* null terminating character */
5184a238c70SJohn Marino   size_t size;                  /* buffer capacity */
5194a238c70SJohn Marino };
5204a238c70SJohn Marino 
5214a238c70SJohn Marino static void
buffer_init(struct string_buffer * b,size_t s)5224a238c70SJohn Marino buffer_init (struct string_buffer *b, size_t s)
5234a238c70SJohn Marino {
5244a238c70SJohn Marino   b->start = (char *) (*__gmp_allocate_func) (s);
5254a238c70SJohn Marino   b->start[0] = '\0';
5264a238c70SJohn Marino   b->curr = b->start;
5274a238c70SJohn Marino   b->size = s;
5284a238c70SJohn Marino }
5294a238c70SJohn Marino 
5304a238c70SJohn Marino /* Increase buffer size by a number of character being the least multiple of
5314a238c70SJohn Marino    4096 greater than LEN+1. */
5324a238c70SJohn Marino static void
buffer_widen(struct string_buffer * b,size_t len)5334a238c70SJohn Marino buffer_widen (struct string_buffer *b, size_t len)
5344a238c70SJohn Marino {
5354a238c70SJohn Marino   const size_t pos = b->curr - b->start;
5364a238c70SJohn Marino   const size_t n = 0x1000 + (len & ~((size_t) 0xfff));
5374a238c70SJohn Marino   MPFR_ASSERTD (pos < b->size);
5384a238c70SJohn Marino 
5394a238c70SJohn Marino   MPFR_ASSERTN ((len & ~((size_t) 4095)) <= (size_t)(SIZE_MAX - 4096));
5404a238c70SJohn Marino   MPFR_ASSERTN (b->size < SIZE_MAX - n);
5414a238c70SJohn Marino 
5424a238c70SJohn Marino   b->start =
5434a238c70SJohn Marino     (char *) (*__gmp_reallocate_func) (b->start, b->size, b->size + n);
5444a238c70SJohn Marino   b->size += n;
5454a238c70SJohn Marino   b->curr = b->start + pos;
5464a238c70SJohn Marino 
5474a238c70SJohn Marino   MPFR_ASSERTD (pos < b->size);
5484a238c70SJohn Marino   MPFR_ASSERTD (*b->curr == '\0');
5494a238c70SJohn Marino }
5504a238c70SJohn Marino 
5514a238c70SJohn Marino /* Concatenate the LEN first characters of the string S to the buffer B and
5524a238c70SJohn Marino    expand it if needed. */
5534a238c70SJohn Marino static void
buffer_cat(struct string_buffer * b,const char * s,size_t len)5544a238c70SJohn Marino buffer_cat (struct string_buffer *b, const char *s, size_t len)
5554a238c70SJohn Marino {
5564a238c70SJohn Marino   MPFR_ASSERTD (len != 0);
5574a238c70SJohn Marino   MPFR_ASSERTD (len <= strlen (s));
5584a238c70SJohn Marino 
5594a238c70SJohn Marino   if (MPFR_UNLIKELY ((b->curr + len) >= (b->start + b->size)))
5604a238c70SJohn Marino     buffer_widen (b, len);
5614a238c70SJohn Marino 
5624a238c70SJohn Marino   strncat (b->curr, s, len);
5634a238c70SJohn Marino   b->curr += len;
5644a238c70SJohn Marino 
5654a238c70SJohn Marino   MPFR_ASSERTD (b->curr < b->start + b->size);
5664a238c70SJohn Marino   MPFR_ASSERTD (*b->curr == '\0');
5674a238c70SJohn Marino }
5684a238c70SJohn Marino 
5694a238c70SJohn Marino /* Add N characters C to the end of buffer B */
5704a238c70SJohn Marino static void
buffer_pad(struct string_buffer * b,const char c,const size_t n)5714a238c70SJohn Marino buffer_pad (struct string_buffer *b, const char c, const size_t n)
5724a238c70SJohn Marino {
5734a238c70SJohn Marino   MPFR_ASSERTD (n != 0);
5744a238c70SJohn Marino 
5754a238c70SJohn Marino   MPFR_ASSERTN (b->size < SIZE_MAX - n - 1);
5764a238c70SJohn Marino   if (MPFR_UNLIKELY ((b->curr + n + 1) > (b->start + b->size)))
5774a238c70SJohn Marino     buffer_widen (b, n);
5784a238c70SJohn Marino 
5794a238c70SJohn Marino   if (n == 1)
5804a238c70SJohn Marino     *b->curr = c;
5814a238c70SJohn Marino   else
5824a238c70SJohn Marino     memset (b->curr, c, n);
5834a238c70SJohn Marino   b->curr += n;
5844a238c70SJohn Marino   *b->curr = '\0';
5854a238c70SJohn Marino 
5864a238c70SJohn Marino   MPFR_ASSERTD (b->curr < b->start + b->size);
5874a238c70SJohn Marino }
5884a238c70SJohn Marino 
5894a238c70SJohn Marino /* Form a string by concatenating the first LEN characters of STR to TZ
5904a238c70SJohn Marino    zero(s), insert into one character C each 3 characters starting from end
5914a238c70SJohn Marino    to begining and concatenate the result to the buffer B. */
5924a238c70SJohn Marino static void
buffer_sandwich(struct string_buffer * b,char * str,size_t len,const size_t tz,const char c)5934a238c70SJohn Marino buffer_sandwich (struct string_buffer *b, char *str, size_t len,
5944a238c70SJohn Marino                  const size_t tz, const char c)
5954a238c70SJohn Marino {
5964a238c70SJohn Marino   const size_t step = 3;
5974a238c70SJohn Marino   const size_t size = len + tz;
5984a238c70SJohn Marino   const size_t r = size % step == 0 ? step : size % step;
5994a238c70SJohn Marino   const size_t q = size % step == 0 ? size / step - 1 : size / step;
6004a238c70SJohn Marino   size_t i;
6014a238c70SJohn Marino 
6024a238c70SJohn Marino   MPFR_ASSERTD (size != 0);
6034a238c70SJohn Marino   if (c == '\0')
6044a238c70SJohn Marino     {
6054a238c70SJohn Marino       buffer_cat (b, str, len);
6064a238c70SJohn Marino       buffer_pad (b, '0', tz);
6074a238c70SJohn Marino       return;
6084a238c70SJohn Marino     }
6094a238c70SJohn Marino 
6104a238c70SJohn Marino   MPFR_ASSERTN (b->size < SIZE_MAX - size - 1 - q);
6114a238c70SJohn Marino   MPFR_ASSERTD (len <= strlen (str));
6124a238c70SJohn Marino   if (MPFR_UNLIKELY ((b->curr + size + 1 + q) > (b->start + b->size)))
6134a238c70SJohn Marino     buffer_widen (b, size + q);
6144a238c70SJohn Marino 
6154a238c70SJohn Marino   /* first R significant digits */
6164a238c70SJohn Marino   memcpy (b->curr, str, r);
6174a238c70SJohn Marino   b->curr += r;
6184a238c70SJohn Marino   str += r;
6194a238c70SJohn Marino   len -= r;
6204a238c70SJohn Marino 
6214a238c70SJohn Marino   /* blocks of thousands. Warning: STR might end in the middle of a block */
6224a238c70SJohn Marino   for (i = 0; i < q; ++i)
6234a238c70SJohn Marino     {
6244a238c70SJohn Marino       *b->curr++ = c;
6254a238c70SJohn Marino       if (MPFR_LIKELY (len > 0))
6264a238c70SJohn Marino         {
6274a238c70SJohn Marino           if (MPFR_LIKELY (len >= step))
6284a238c70SJohn Marino             /* step significant digits */
6294a238c70SJohn Marino             {
6304a238c70SJohn Marino               memcpy (b->curr, str, step);
6314a238c70SJohn Marino               len -= step;
6324a238c70SJohn Marino             }
6334a238c70SJohn Marino           else
6344a238c70SJohn Marino             /* last digits in STR, fill up thousand block with zeros */
6354a238c70SJohn Marino             {
6364a238c70SJohn Marino               memcpy (b->curr, str, len);
6374a238c70SJohn Marino               memset (b->curr + len, '0', step - len);
6384a238c70SJohn Marino               len = 0;
6394a238c70SJohn Marino             }
6404a238c70SJohn Marino         }
6414a238c70SJohn Marino       else
6424a238c70SJohn Marino         /* trailing zeros */
6434a238c70SJohn Marino         memset (b->curr, '0', step);
6444a238c70SJohn Marino 
6454a238c70SJohn Marino       b->curr += step;
6464a238c70SJohn Marino       str += step;
6474a238c70SJohn Marino     }
6484a238c70SJohn Marino 
6494a238c70SJohn Marino   *b->curr = '\0';
6504a238c70SJohn Marino 
6514a238c70SJohn Marino   MPFR_ASSERTD (b->curr < b->start + b->size);
6524a238c70SJohn Marino }
6534a238c70SJohn Marino 
6544a238c70SJohn Marino /* let gmp_xprintf process the part it can understand */
6554a238c70SJohn Marino static int
sprntf_gmp(struct string_buffer * b,const char * fmt,va_list ap)6564a238c70SJohn Marino sprntf_gmp (struct string_buffer *b, const char *fmt, va_list ap)
6574a238c70SJohn Marino {
6584a238c70SJohn Marino   int length;
6594a238c70SJohn Marino   char *s;
6604a238c70SJohn Marino 
6614a238c70SJohn Marino   length = gmp_vasprintf (&s, fmt, ap);
6624a238c70SJohn Marino   if (length > 0)
6634a238c70SJohn Marino     buffer_cat (b, s, length);
6644a238c70SJohn Marino 
6654a238c70SJohn Marino   mpfr_free_str (s);
6664a238c70SJohn Marino   return length;
6674a238c70SJohn Marino }
6684a238c70SJohn Marino 
6694a238c70SJohn Marino /* Helper struct and functions for temporary strings management */
6704a238c70SJohn Marino /* struct for easy string clearing */
6714a238c70SJohn Marino struct string_list
6724a238c70SJohn Marino {
6734a238c70SJohn Marino   char *string;
6744a238c70SJohn Marino   struct string_list *next; /* NULL in last node */
6754a238c70SJohn Marino };
6764a238c70SJohn Marino 
6774a238c70SJohn Marino /* initialisation */
6784a238c70SJohn Marino static void
init_string_list(struct string_list * sl)6794a238c70SJohn Marino init_string_list (struct string_list *sl)
6804a238c70SJohn Marino {
6814a238c70SJohn Marino   sl->string = NULL;
6824a238c70SJohn Marino   sl->next = NULL;
6834a238c70SJohn Marino }
6844a238c70SJohn Marino 
6854a238c70SJohn Marino /* clear all strings in the list */
6864a238c70SJohn Marino static void
clear_string_list(struct string_list * sl)6874a238c70SJohn Marino clear_string_list (struct string_list *sl)
6884a238c70SJohn Marino {
6894a238c70SJohn Marino   struct string_list *n;
6904a238c70SJohn Marino 
6914a238c70SJohn Marino   while (sl)
6924a238c70SJohn Marino     {
6934a238c70SJohn Marino       if (sl->string)
6944a238c70SJohn Marino         mpfr_free_str (sl->string);
6954a238c70SJohn Marino       n = sl->next;
6964a238c70SJohn Marino       (*__gmp_free_func) (sl, sizeof(struct string_list));
6974a238c70SJohn Marino       sl = n;
6984a238c70SJohn Marino     }
6994a238c70SJohn Marino }
7004a238c70SJohn Marino 
7014a238c70SJohn Marino /* add a string in the list */
7024a238c70SJohn Marino static char *
register_string(struct string_list * sl,char * new_string)7034a238c70SJohn Marino register_string (struct string_list *sl, char *new_string)
7044a238c70SJohn Marino {
7054a238c70SJohn Marino   /* look for the last node */
7064a238c70SJohn Marino   while (sl->next)
7074a238c70SJohn Marino     sl = sl->next;
7084a238c70SJohn Marino 
7094a238c70SJohn Marino   sl->next = (struct string_list*)
7104a238c70SJohn Marino     (*__gmp_allocate_func) (sizeof (struct string_list));
7114a238c70SJohn Marino 
7124a238c70SJohn Marino   sl = sl->next;
7134a238c70SJohn Marino   sl->next = NULL;
7144a238c70SJohn Marino   return sl->string = new_string;
7154a238c70SJohn Marino }
7164a238c70SJohn Marino 
7174a238c70SJohn Marino /* padding type: where are the padding characters */
7184a238c70SJohn Marino enum pad_t
7194a238c70SJohn Marino   {
7204a238c70SJohn Marino     LEFT,          /* spaces in left hand side for right justification */
7214a238c70SJohn Marino     LEADING_ZEROS, /* padding with '0' characters in integral part */
7224a238c70SJohn Marino     RIGHT          /* spaces in right hand side for left justification */
7234a238c70SJohn Marino   };
7244a238c70SJohn Marino 
7254a238c70SJohn Marino /* number_parts details how much characters are needed in each part of a float
7264a238c70SJohn Marino    print.  */
7274a238c70SJohn Marino struct number_parts
7284a238c70SJohn Marino {
7294a238c70SJohn Marino   enum pad_t pad_type;    /* Padding type */
7304a238c70SJohn Marino   size_t pad_size;        /* Number of padding characters */
7314a238c70SJohn Marino 
7324a238c70SJohn Marino   char sign;              /* Sign character */
7334a238c70SJohn Marino 
7344a238c70SJohn Marino   char *prefix_ptr;       /* Pointer to prefix part */
7354a238c70SJohn Marino   size_t prefix_size;     /* Number of characters in *prefix_ptr */
7364a238c70SJohn Marino 
7374a238c70SJohn Marino   char thousands_sep;     /* Thousands separator (only with style 'f') */
7384a238c70SJohn Marino 
7394a238c70SJohn Marino   char *ip_ptr;           /* Pointer to integral part characters*/
7404a238c70SJohn Marino   size_t ip_size;         /* Number of digits in *ip_ptr */
7414a238c70SJohn Marino   int ip_trailing_zeros;  /* Number of additional null digits in integral
7424a238c70SJohn Marino                              part */
7434a238c70SJohn Marino 
7444a238c70SJohn Marino   char point;             /* Decimal point character */
7454a238c70SJohn Marino 
7464a238c70SJohn Marino   int fp_leading_zeros;   /* Number of additional leading zeros in fractional
7474a238c70SJohn Marino                              part */
7484a238c70SJohn Marino   char *fp_ptr;           /* Pointer to fractional part characters */
7494a238c70SJohn Marino   size_t fp_size;         /* Number of digits in *fp_ptr */
7504a238c70SJohn Marino   int fp_trailing_zeros;  /* Number of additional trailing zeros in fractional
7514a238c70SJohn Marino                              part */
7524a238c70SJohn Marino 
7534a238c70SJohn Marino   char *exp_ptr;          /* Pointer to exponent part */
7544a238c70SJohn Marino   size_t exp_size;        /* Number of characters in *exp_ptr */
7554a238c70SJohn Marino 
7564a238c70SJohn Marino   struct string_list *sl; /* List of string buffers in use: we need such a
7574a238c70SJohn Marino                              mechanism because fp_ptr may point into the same
7584a238c70SJohn Marino                              string as ip_ptr */
7594a238c70SJohn Marino };
7604a238c70SJohn Marino 
7614a238c70SJohn Marino /* For a real non zero number x, what is the base exponent f when rounding x
7624a238c70SJohn Marino    with rounding mode r to r(x) = m*b^f, where m is a digit and 1 <= m < b ?
7634a238c70SJohn Marino    Return non zero value if x is rounded up to b^f, return zero otherwise */
7644a238c70SJohn Marino static int
next_base_power_p(mpfr_srcptr x,int base,mpfr_rnd_t rnd)7654a238c70SJohn Marino next_base_power_p (mpfr_srcptr x, int base, mpfr_rnd_t rnd)
7664a238c70SJohn Marino {
7674a238c70SJohn Marino   mpfr_prec_t nbits;
7684a238c70SJohn Marino   mp_limb_t pm;
7694a238c70SJohn Marino   mp_limb_t xm;
7704a238c70SJohn Marino 
7714a238c70SJohn Marino   MPFR_ASSERTD (MPFR_IS_PURE_FP (x));
7724a238c70SJohn Marino   MPFR_ASSERTD (base == 2 || base == 16);
7734a238c70SJohn Marino 
7744a238c70SJohn Marino   /* Warning: the decimal point is AFTER THE FIRST DIGIT in this output
7754a238c70SJohn Marino      representation. */
7764a238c70SJohn Marino   nbits = base == 2 ? 1 : 4;
7774a238c70SJohn Marino 
7784a238c70SJohn Marino   if (rnd == MPFR_RNDZ
7794a238c70SJohn Marino       || (rnd == MPFR_RNDD && MPFR_IS_POS (x))
7804a238c70SJohn Marino       || (rnd == MPFR_RNDU && MPFR_IS_NEG (x))
7814a238c70SJohn Marino       || MPFR_PREC (x) <= nbits)
7824a238c70SJohn Marino     /* no rounding when printing x with 1 digit */
7834a238c70SJohn Marino     return 0;
7844a238c70SJohn Marino 
7854a238c70SJohn Marino   xm = MPFR_MANT (x) [MPFR_LIMB_SIZE (x) - 1];
7864a238c70SJohn Marino   pm = MPFR_LIMB_MASK (GMP_NUMB_BITS - nbits);
7874a238c70SJohn Marino   if ((xm & ~pm) ^ ~pm)
7884a238c70SJohn Marino     /* do no round up if some of the nbits first bits are 0s. */
7894a238c70SJohn Marino     return 0;
7904a238c70SJohn Marino 
7914a238c70SJohn Marino   if (rnd == MPFR_RNDN)
7924a238c70SJohn Marino     /* mask for rounding bit */
7934a238c70SJohn Marino     pm = (MPFR_LIMB_ONE << (GMP_NUMB_BITS - nbits - 1));
7944a238c70SJohn Marino 
7954a238c70SJohn Marino   /* round up if some remaining bits are 1 */
7964a238c70SJohn Marino   /* warning: the return value must be an int */
7974a238c70SJohn Marino   return xm & pm ? 1 : 0;
7984a238c70SJohn Marino }
7994a238c70SJohn Marino 
8004a238c70SJohn Marino /* Record information from mpfr_get_str() so as to avoid multiple
8014a238c70SJohn Marino    calls to this expensive function. */
8024a238c70SJohn Marino struct decimal_info
8034a238c70SJohn Marino {
8044a238c70SJohn Marino   mpfr_exp_t exp;
8054a238c70SJohn Marino   char *str;
8064a238c70SJohn Marino };
8074a238c70SJohn Marino 
8084a238c70SJohn Marino /* For a real non zero number x, what is the exponent f so that
8094a238c70SJohn Marino    10^f <= x < 10^(f+1). */
8104a238c70SJohn Marino static mpfr_exp_t
floor_log10(mpfr_srcptr x)8114a238c70SJohn Marino floor_log10 (mpfr_srcptr x)
8124a238c70SJohn Marino {
8134a238c70SJohn Marino   mpfr_t y;
8144a238c70SJohn Marino   mpfr_exp_t exp;
8154a238c70SJohn Marino 
8164a238c70SJohn Marino   /* make sure first that y can represent a mpfr_exp_t exactly
8174a238c70SJohn Marino      and can compare with x */
8184a238c70SJohn Marino   mpfr_prec_t prec = sizeof (mpfr_exp_t) * CHAR_BIT;
8194a238c70SJohn Marino   mpfr_init2 (y, MAX (prec, MPFR_PREC (x)));
8204a238c70SJohn Marino 
8214a238c70SJohn Marino   exp = mpfr_ceil_mul (MPFR_GET_EXP (x), 10, 1) - 1;
8224a238c70SJohn Marino   mpfr_set_exp_t (y, exp, MPFR_RNDU);
8234a238c70SJohn Marino   /* The following call to mpfr_ui_pow should be fast: y is an integer
8244a238c70SJohn Marino      (not too large), so that mpfr_pow_z will be used internally. */
8254a238c70SJohn Marino   mpfr_ui_pow (y, 10, y, MPFR_RNDU);
8264a238c70SJohn Marino   if (mpfr_cmpabs (x, y) < 0)
8274a238c70SJohn Marino     exp--;
8284a238c70SJohn Marino 
8294a238c70SJohn Marino   mpfr_clear (y);
8304a238c70SJohn Marino   return exp;
8314a238c70SJohn Marino }
8324a238c70SJohn Marino 
8334a238c70SJohn Marino /* Determine the different parts of the string representation of the regular
8344a238c70SJohn Marino    number P when SPEC.SPEC is 'a', 'A', or 'b'.
8354a238c70SJohn Marino 
8364a238c70SJohn Marino    return -1 if some field > INT_MAX */
8374a238c70SJohn Marino static int
regular_ab(struct number_parts * np,mpfr_srcptr p,const struct printf_spec spec)8384a238c70SJohn Marino regular_ab (struct number_parts *np, mpfr_srcptr p,
8394a238c70SJohn Marino             const struct printf_spec spec)
8404a238c70SJohn Marino {
8414a238c70SJohn Marino   int uppercase;
8424a238c70SJohn Marino   int base;
8434a238c70SJohn Marino   char *str;
8444a238c70SJohn Marino   mpfr_exp_t exp;
8454a238c70SJohn Marino 
8464a238c70SJohn Marino   uppercase = spec.spec == 'A';
8474a238c70SJohn Marino 
8484a238c70SJohn Marino   /* sign */
8494a238c70SJohn Marino   if (MPFR_IS_NEG (p))
8504a238c70SJohn Marino     np->sign = '-';
8514a238c70SJohn Marino   else if (spec.showsign || spec.space)
8524a238c70SJohn Marino     np->sign = spec.showsign ? '+' : ' ';
8534a238c70SJohn Marino 
8544a238c70SJohn Marino   if (spec.spec == 'a' || spec.spec == 'A')
8554a238c70SJohn Marino     /* prefix part */
8564a238c70SJohn Marino     {
8574a238c70SJohn Marino       np->prefix_size = 2;
8584a238c70SJohn Marino       str = (char *) (*__gmp_allocate_func) (1 + np->prefix_size);
8594a238c70SJohn Marino       str[0] = '0';
8604a238c70SJohn Marino       str[1] = uppercase ? 'X' : 'x';
8614a238c70SJohn Marino       str[2] = '\0';
8624a238c70SJohn Marino       np->prefix_ptr = register_string (np->sl, str);
8634a238c70SJohn Marino     }
8644a238c70SJohn Marino 
8654a238c70SJohn Marino   /* integral part */
8664a238c70SJohn Marino   np->ip_size = 1;
8674a238c70SJohn Marino   base = (spec.spec == 'b') ? 2 : 16;
8684a238c70SJohn Marino 
8694a238c70SJohn Marino   if (spec.prec != 0)
8704a238c70SJohn Marino     {
8714a238c70SJohn Marino       size_t nsd;
8724a238c70SJohn Marino 
8734a238c70SJohn Marino       /* Number of significant digits:
8744a238c70SJohn Marino          - if no given precision, let mpfr_get_str determine it;
8754a238c70SJohn Marino          - if a non-zero precision is specified, then one digit before decimal
8764a238c70SJohn Marino          point plus SPEC.PREC after it. */
8774a238c70SJohn Marino       nsd = spec.prec < 0 ? 0 : spec.prec + np->ip_size;
8784a238c70SJohn Marino       str = mpfr_get_str (0, &exp, base, nsd, p, spec.rnd_mode);
8794a238c70SJohn Marino       register_string (np->sl, str);
8804a238c70SJohn Marino       np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str;  /* skip sign if any */
8814a238c70SJohn Marino 
8824a238c70SJohn Marino       if (base == 16)
8834a238c70SJohn Marino         /* EXP is the exponent for radix sixteen with decimal point BEFORE the
8844a238c70SJohn Marino            first digit, we want the exponent for radix two and the decimal
8854a238c70SJohn Marino            point AFTER the first digit. */
8864a238c70SJohn Marino         {
8874a238c70SJohn Marino           MPFR_ASSERTN (exp > MPFR_EMIN_MIN /4); /* possible overflow */
8884a238c70SJohn Marino           exp = (exp - 1) * 4;
8894a238c70SJohn Marino         }
8904a238c70SJohn Marino       else
8914a238c70SJohn Marino         /* EXP is the exponent for decimal point BEFORE the first digit, we
8924a238c70SJohn Marino            want the exponent for decimal point AFTER the first digit. */
8934a238c70SJohn Marino         {
8944a238c70SJohn Marino           MPFR_ASSERTN (exp > MPFR_EMIN_MIN); /* possible overflow */
8954a238c70SJohn Marino           --exp;
8964a238c70SJohn Marino         }
8974a238c70SJohn Marino     }
8984a238c70SJohn Marino   else if (next_base_power_p (p, base, spec.rnd_mode))
8994a238c70SJohn Marino     {
9004a238c70SJohn Marino       str = (char *)(*__gmp_allocate_func) (2);
9014a238c70SJohn Marino       str[0] = '1';
9024a238c70SJohn Marino       str[1] = '\0';
9034a238c70SJohn Marino       np->ip_ptr = register_string (np->sl, str);
9044a238c70SJohn Marino 
9054a238c70SJohn Marino       exp = MPFR_GET_EXP (p);
9064a238c70SJohn Marino     }
9074a238c70SJohn Marino   else if (base == 2)
9084a238c70SJohn Marino     {
9094a238c70SJohn Marino       str = (char *)(*__gmp_allocate_func) (2);
9104a238c70SJohn Marino       str[0] = '1';
9114a238c70SJohn Marino       str[1] = '\0';
9124a238c70SJohn Marino       np->ip_ptr = register_string (np->sl, str);
9134a238c70SJohn Marino 
9144a238c70SJohn Marino       exp = MPFR_GET_EXP (p) - 1;
9154a238c70SJohn Marino     }
9164a238c70SJohn Marino   else
9174a238c70SJohn Marino     {
9184a238c70SJohn Marino       int digit;
9194a238c70SJohn Marino       mp_limb_t msl = MPFR_MANT (p)[MPFR_LIMB_SIZE (p) - 1];
9204a238c70SJohn Marino       int rnd_bit = GMP_NUMB_BITS - 5;
9214a238c70SJohn Marino 
9224a238c70SJohn Marino       /* pick up the 4 first bits */
9234a238c70SJohn Marino       digit = msl >> (rnd_bit+1);
9244a238c70SJohn Marino       if (spec.rnd_mode == MPFR_RNDA
9254a238c70SJohn Marino           || (spec.rnd_mode == MPFR_RNDU && MPFR_IS_POS (p))
9264a238c70SJohn Marino           || (spec.rnd_mode == MPFR_RNDD && MPFR_IS_NEG (p))
9274a238c70SJohn Marino           || (spec.rnd_mode == MPFR_RNDN
9284a238c70SJohn Marino               && (msl & (MPFR_LIMB_ONE << rnd_bit))))
9294a238c70SJohn Marino         digit++;
9304a238c70SJohn Marino       MPFR_ASSERTD ((0 <= digit) && (digit <= 15));
9314a238c70SJohn Marino 
9324a238c70SJohn Marino       str = (char *)(*__gmp_allocate_func) (1 + np->ip_size);
9334a238c70SJohn Marino       str[0] = num_to_text [digit];
9344a238c70SJohn Marino       str[1] = '\0';
9354a238c70SJohn Marino       np->ip_ptr = register_string (np->sl, str);
9364a238c70SJohn Marino 
9374a238c70SJohn Marino       exp = MPFR_GET_EXP (p) - 4;
9384a238c70SJohn Marino     }
9394a238c70SJohn Marino 
9404a238c70SJohn Marino   if (uppercase)
9414a238c70SJohn Marino     /* All digits in upper case */
9424a238c70SJohn Marino     {
9434a238c70SJohn Marino       char *s1 = str;
9444a238c70SJohn Marino       while (*s1)
9454a238c70SJohn Marino         {
9464a238c70SJohn Marino           switch (*s1)
9474a238c70SJohn Marino             {
9484a238c70SJohn Marino             case 'a':
9494a238c70SJohn Marino               *s1 = 'A';
9504a238c70SJohn Marino               break;
9514a238c70SJohn Marino             case 'b':
9524a238c70SJohn Marino               *s1 = 'B';
9534a238c70SJohn Marino               break;
9544a238c70SJohn Marino             case 'c':
9554a238c70SJohn Marino               *s1 = 'C';
9564a238c70SJohn Marino               break;
9574a238c70SJohn Marino             case 'd':
9584a238c70SJohn Marino               *s1 = 'D';
9594a238c70SJohn Marino               break;
9604a238c70SJohn Marino             case 'e':
9614a238c70SJohn Marino               *s1 = 'E';
9624a238c70SJohn Marino               break;
9634a238c70SJohn Marino             case 'f':
9644a238c70SJohn Marino               *s1 = 'F';
9654a238c70SJohn Marino               break;
9664a238c70SJohn Marino             }
9674a238c70SJohn Marino           s1++;
9684a238c70SJohn Marino         }
9694a238c70SJohn Marino     }
9704a238c70SJohn Marino 
9714a238c70SJohn Marino   if (spec.spec == 'b' || spec.prec != 0)
9724a238c70SJohn Marino     /* compute the number of digits in fractional part */
9734a238c70SJohn Marino     {
9744a238c70SJohn Marino       char *ptr;
9754a238c70SJohn Marino       size_t str_len;
9764a238c70SJohn Marino 
9774a238c70SJohn Marino       /* the sign has been skipped, skip also the first digit */
9784a238c70SJohn Marino       ++str;
9794a238c70SJohn Marino       str_len = strlen (str);
9804a238c70SJohn Marino       ptr = str + str_len - 1; /* points to the end of str */
9814a238c70SJohn Marino 
9824a238c70SJohn Marino       if (spec.prec < 0)
9834a238c70SJohn Marino         /* remove trailing zeros, if any */
9844a238c70SJohn Marino         {
9854a238c70SJohn Marino           while ((*ptr == '0') && (str_len != 0))
9864a238c70SJohn Marino             {
9874a238c70SJohn Marino               --ptr;
9884a238c70SJohn Marino               --str_len;
9894a238c70SJohn Marino             }
9904a238c70SJohn Marino         }
9914a238c70SJohn Marino 
9924a238c70SJohn Marino       if (str_len > INT_MAX)
9934a238c70SJohn Marino         /* too many digits in fractional part */
9944a238c70SJohn Marino         return -1;
9954a238c70SJohn Marino 
9964a238c70SJohn Marino       if (str_len != 0)
9974a238c70SJohn Marino         /* there are some non-zero digits in fractional part */
9984a238c70SJohn Marino         {
9994a238c70SJohn Marino           np->fp_ptr = str;
10004a238c70SJohn Marino           np->fp_size = str_len;
10014a238c70SJohn Marino           if ((int) str_len < spec.prec)
10024a238c70SJohn Marino             np->fp_trailing_zeros = spec.prec - str_len;
10034a238c70SJohn Marino         }
10044a238c70SJohn Marino     }
10054a238c70SJohn Marino 
10064a238c70SJohn Marino   /* decimal point */
10074a238c70SJohn Marino   if ((np->fp_size != 0) || spec.alt)
10084a238c70SJohn Marino     np->point = MPFR_DECIMAL_POINT;
10094a238c70SJohn Marino 
10104a238c70SJohn Marino   /* the exponent part contains the character 'p', or 'P' plus the sign
10114a238c70SJohn Marino      character plus at least one digit and only as many more digits as
10124a238c70SJohn Marino      necessary to represent the exponent.
10134a238c70SJohn Marino      We assume that |EXP| < 10^INT_MAX. */
10144a238c70SJohn Marino   np->exp_size = 3;
10154a238c70SJohn Marino   {
10164a238c70SJohn Marino     mpfr_uexp_t x;
10174a238c70SJohn Marino 
10184a238c70SJohn Marino     x = SAFE_ABS (mpfr_uexp_t, exp);
10194a238c70SJohn Marino     while (x > 9)
10204a238c70SJohn Marino       {
10214a238c70SJohn Marino         np->exp_size++;
10224a238c70SJohn Marino         x /= 10;
10234a238c70SJohn Marino       }
10244a238c70SJohn Marino   }
10254a238c70SJohn Marino   str = (char *) (*__gmp_allocate_func) (1 + np->exp_size);
10264a238c70SJohn Marino   np->exp_ptr = register_string (np->sl, str);
10274a238c70SJohn Marino   {
10284a238c70SJohn Marino     char exp_fmt[8];  /* contains at most 7 characters like in "p%+.1i",
10294a238c70SJohn Marino                          or "P%+.2li" */
10304a238c70SJohn Marino 
10314a238c70SJohn Marino     exp_fmt[0] = uppercase ? 'P' : 'p';
10324a238c70SJohn Marino     exp_fmt[1] = '\0';
10334a238c70SJohn Marino     strcat (exp_fmt, "%+.1" MPFR_EXP_FSPEC "d");
10344a238c70SJohn Marino 
10354a238c70SJohn Marino     if (sprintf (str, exp_fmt, (mpfr_eexp_t) exp) < 0)
10364a238c70SJohn Marino       return -1;
10374a238c70SJohn Marino   }
10384a238c70SJohn Marino 
10394a238c70SJohn Marino   return 0;
10404a238c70SJohn Marino }
10414a238c70SJohn Marino 
10424a238c70SJohn Marino /* Determine the different parts of the string representation of the regular
10434a238c70SJohn Marino    number P when SPEC.SPEC is 'e', 'E', 'g', or 'G'.
10444a238c70SJohn Marino    DEC_INFO contains the previously computed exponent and string or is NULL.
10454a238c70SJohn Marino 
10464a238c70SJohn Marino    return -1 if some field > INT_MAX */
10474a238c70SJohn Marino static int
regular_eg(struct number_parts * np,mpfr_srcptr p,const struct printf_spec spec,struct decimal_info * dec_info)10484a238c70SJohn Marino regular_eg (struct number_parts *np, mpfr_srcptr p,
10494a238c70SJohn Marino             const struct printf_spec spec, struct decimal_info *dec_info)
10504a238c70SJohn Marino {
10514a238c70SJohn Marino   char *str;
10524a238c70SJohn Marino   mpfr_exp_t exp;
10534a238c70SJohn Marino 
10544a238c70SJohn Marino   const int uppercase = spec.spec == 'E' || spec.spec == 'G';
10554a238c70SJohn Marino   const int spec_g = spec.spec == 'g' || spec.spec == 'G';
10564a238c70SJohn Marino   const int keep_trailing_zeros = (spec_g && spec.alt)
10574a238c70SJohn Marino     || (!spec_g && (spec.prec > 0));
10584a238c70SJohn Marino 
10594a238c70SJohn Marino   /* sign */
10604a238c70SJohn Marino   if (MPFR_IS_NEG (p))
10614a238c70SJohn Marino     np->sign = '-';
10624a238c70SJohn Marino   else if (spec.showsign || spec.space)
10634a238c70SJohn Marino     np->sign = spec.showsign ? '+' : ' ';
10644a238c70SJohn Marino 
10654a238c70SJohn Marino   /* integral part */
10664a238c70SJohn Marino   np->ip_size = 1;
10674a238c70SJohn Marino   if (dec_info == NULL)
10684a238c70SJohn Marino     {
10694a238c70SJohn Marino       size_t nsd;
10704a238c70SJohn Marino 
10714a238c70SJohn Marino       /* Number of significant digits:
10724a238c70SJohn Marino          - if no given precision, then let mpfr_get_str determine it,
10734a238c70SJohn Marino          - if a precision is specified, then one digit before decimal point
10744a238c70SJohn Marino          plus SPEC.PREC after it.
10754a238c70SJohn Marino          We use the fact here that mpfr_get_str allows us to ask for only one
10764a238c70SJohn Marino          significant digit when the base is not a power of 2. */
10774a238c70SJohn Marino       nsd = (spec.prec < 0) ? 0 : spec.prec + np->ip_size;
10784a238c70SJohn Marino       str = mpfr_get_str (0, &exp, 10, nsd, p, spec.rnd_mode);
10794a238c70SJohn Marino       register_string (np->sl, str);
10804a238c70SJohn Marino     }
10814a238c70SJohn Marino   else
10824a238c70SJohn Marino     {
10834a238c70SJohn Marino       exp = dec_info->exp;
10844a238c70SJohn Marino       str = dec_info->str;
10854a238c70SJohn Marino     }
10864a238c70SJohn Marino   np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str;  /* skip sign if any */
10874a238c70SJohn Marino 
10884a238c70SJohn Marino   if (spec.prec != 0)
10894a238c70SJohn Marino     /* compute the number of digits in fractional part */
10904a238c70SJohn Marino     {
10914a238c70SJohn Marino       char *ptr;
10924a238c70SJohn Marino       size_t str_len;
10934a238c70SJohn Marino 
10944a238c70SJohn Marino       /* the sign has been skipped, skip also the first digit */
10954a238c70SJohn Marino       ++str;
10964a238c70SJohn Marino       str_len = strlen (str);
10974a238c70SJohn Marino       ptr = str + str_len - 1; /* points to the end of str */
10984a238c70SJohn Marino 
10994a238c70SJohn Marino       if (!keep_trailing_zeros)
11004a238c70SJohn Marino         /* remove trailing zeros, if any */
11014a238c70SJohn Marino         {
11024a238c70SJohn Marino           while ((*ptr == '0') && (str_len != 0))
11034a238c70SJohn Marino             {
11044a238c70SJohn Marino               --ptr;
11054a238c70SJohn Marino               --str_len;
11064a238c70SJohn Marino             }
11074a238c70SJohn Marino         }
11084a238c70SJohn Marino 
11094a238c70SJohn Marino       if (str_len > INT_MAX)
11104a238c70SJohn Marino         /* too many digits in fractional part */
11114a238c70SJohn Marino         return -1;
11124a238c70SJohn Marino 
11134a238c70SJohn Marino       if (str_len != 0)
11144a238c70SJohn Marino         /* there are some non-zero digits in fractional part */
11154a238c70SJohn Marino         {
11164a238c70SJohn Marino           np->fp_ptr = str;
11174a238c70SJohn Marino           np->fp_size = str_len;
11184a238c70SJohn Marino           if ((!spec_g || spec.alt) && (spec.prec > 0)
11194a238c70SJohn Marino               && ((int)str_len < spec.prec))
11204a238c70SJohn Marino             /* add missing trailing zeros */
11214a238c70SJohn Marino             np->fp_trailing_zeros = spec.prec - str_len;
11224a238c70SJohn Marino         }
11234a238c70SJohn Marino     }
11244a238c70SJohn Marino 
11254a238c70SJohn Marino   /* decimal point */
11264a238c70SJohn Marino   if (np->fp_size != 0 || spec.alt)
11274a238c70SJohn Marino     np->point = MPFR_DECIMAL_POINT;
11284a238c70SJohn Marino 
11294a238c70SJohn Marino   /* EXP is the exponent for decimal point BEFORE the first digit, we want
11304a238c70SJohn Marino      the exponent for decimal point AFTER the first digit.
11314a238c70SJohn Marino      Here, no possible overflow because exp < MPFR_EXP (p) / 3 */
11324a238c70SJohn Marino   exp--;
11334a238c70SJohn Marino 
11344a238c70SJohn Marino   /* the exponent part contains the character 'e', or 'E' plus the sign
11354a238c70SJohn Marino      character plus at least two digits and only as many more digits as
11364a238c70SJohn Marino      necessary to represent the exponent.
11374a238c70SJohn Marino      We assume that |EXP| < 10^INT_MAX. */
11384a238c70SJohn Marino   np->exp_size = 3;
11394a238c70SJohn Marino   {
11404a238c70SJohn Marino     mpfr_uexp_t x;
11414a238c70SJohn Marino 
11424a238c70SJohn Marino     x = SAFE_ABS (mpfr_uexp_t, exp);
11434a238c70SJohn Marino     while (x > 9)
11444a238c70SJohn Marino       {
11454a238c70SJohn Marino         np->exp_size++;
11464a238c70SJohn Marino         x /= 10;
11474a238c70SJohn Marino       }
11484a238c70SJohn Marino   }
11494a238c70SJohn Marino   if (np->exp_size < 4)
11504a238c70SJohn Marino     np->exp_size = 4;
11514a238c70SJohn Marino 
11524a238c70SJohn Marino   str = (char *) (*__gmp_allocate_func) (1 + np->exp_size);
11534a238c70SJohn Marino   np->exp_ptr = register_string (np->sl, str);
11544a238c70SJohn Marino 
11554a238c70SJohn Marino   {
11564a238c70SJohn Marino     char exp_fmt[8];  /* e.g. "e%+.2i", or "E%+.2li" */
11574a238c70SJohn Marino 
11584a238c70SJohn Marino     exp_fmt[0] = uppercase ? 'E' : 'e';
11594a238c70SJohn Marino     exp_fmt[1] = '\0';
11604a238c70SJohn Marino     strcat (exp_fmt, "%+.2" MPFR_EXP_FSPEC "d");
11614a238c70SJohn Marino 
11624a238c70SJohn Marino     if (sprintf (str, exp_fmt, (mpfr_eexp_t) exp) < 0)
11634a238c70SJohn Marino       return -1;
11644a238c70SJohn Marino   }
11654a238c70SJohn Marino 
11664a238c70SJohn Marino   return 0;
11674a238c70SJohn Marino }
11684a238c70SJohn Marino 
11694a238c70SJohn Marino /* Determine the different parts of the string representation of the regular
11704a238c70SJohn Marino    number P when SPEC.SPEC is 'f', 'F', 'g', or 'G'.
11714a238c70SJohn Marino    DEC_INFO contains the previously computed exponent and string or is NULL.
11724a238c70SJohn Marino 
11734a238c70SJohn Marino    return -1 if some field of number_parts is greater than INT_MAX */
11744a238c70SJohn Marino static int
regular_fg(struct number_parts * np,mpfr_srcptr p,const struct printf_spec spec,struct decimal_info * dec_info)11754a238c70SJohn Marino regular_fg (struct number_parts *np, mpfr_srcptr p,
11764a238c70SJohn Marino             const struct printf_spec spec, struct decimal_info *dec_info)
11774a238c70SJohn Marino {
11784a238c70SJohn Marino   mpfr_exp_t exp;
11794a238c70SJohn Marino   char * str;
11804a238c70SJohn Marino   const int spec_g = (spec.spec == 'g' || spec.spec == 'G');
1181ab6d115fSJohn Marino   const int keep_trailing_zeros = !spec_g || spec.alt;
11824a238c70SJohn Marino 
11834a238c70SJohn Marino   /* WARNING: an empty precision field is forbidden (it means precision = 6
11844a238c70SJohn Marino      and it should have been changed to 6 before the function call) */
11854a238c70SJohn Marino   MPFR_ASSERTD (spec.prec >= 0);
11864a238c70SJohn Marino 
11874a238c70SJohn Marino   /* sign */
11884a238c70SJohn Marino   if (MPFR_IS_NEG (p))
11894a238c70SJohn Marino     np->sign = '-';
11904a238c70SJohn Marino   else if (spec.showsign || spec.space)
11914a238c70SJohn Marino     np->sign = spec.showsign ? '+' : ' ';
11924a238c70SJohn Marino 
11934a238c70SJohn Marino   if (MPFR_GET_EXP (p) <= 0)
11944a238c70SJohn Marino     /* 0 < |p| < 1 */
11954a238c70SJohn Marino     {
11964a238c70SJohn Marino       /* Most of the time, integral part is 0 */
11974a238c70SJohn Marino       np->ip_size = 1;
11984a238c70SJohn Marino       str = (char *) (*__gmp_allocate_func) (1 + np->ip_size);
11994a238c70SJohn Marino       str[0] = '0';
12004a238c70SJohn Marino       str[1] = '\0';
12014a238c70SJohn Marino       np->ip_ptr = register_string (np->sl, str);
12024a238c70SJohn Marino 
12034a238c70SJohn Marino       if (spec.prec == 0)
12044a238c70SJohn Marino         /* only two possibilities: either 1 or 0. */
12054a238c70SJohn Marino         {
12064a238c70SJohn Marino           mpfr_t y;
12074a238c70SJohn Marino           /* y = abs(p) */
12084a238c70SJohn Marino           MPFR_ALIAS (y, p, 1, MPFR_EXP (p));
12094a238c70SJohn Marino 
12104a238c70SJohn Marino           if (spec.rnd_mode == MPFR_RNDA
12114a238c70SJohn Marino               || (spec.rnd_mode == MPFR_RNDD && MPFR_IS_NEG (p))
12124a238c70SJohn Marino               || (spec.rnd_mode == MPFR_RNDU && MPFR_IS_POS (p))
12134a238c70SJohn Marino               || (spec.rnd_mode == MPFR_RNDN && mpfr_cmp_d (y, 0.5) > 0))
12144a238c70SJohn Marino             /* rounded up to 1: one digit '1' in integral part.
12154a238c70SJohn Marino                note that 0.5 is rounded to 0 with RNDN (round ties to even) */
12164a238c70SJohn Marino             np->ip_ptr[0] = '1';
12174a238c70SJohn Marino         }
12184a238c70SJohn Marino       else
12194a238c70SJohn Marino         {
12204a238c70SJohn Marino           /* exp =  position of the most significant decimal digit. */
12214a238c70SJohn Marino           exp = floor_log10 (p);
12224a238c70SJohn Marino           MPFR_ASSERTD (exp < 0);
12234a238c70SJohn Marino 
12244a238c70SJohn Marino           if (exp < -spec.prec)
12254a238c70SJohn Marino             /* only the last digit may be non zero */
12264a238c70SJohn Marino             {
12274a238c70SJohn Marino               int round_away;
12284a238c70SJohn Marino               switch (spec.rnd_mode)
12294a238c70SJohn Marino                 {
12304a238c70SJohn Marino                 case MPFR_RNDA:
12314a238c70SJohn Marino                   round_away = 1;
12324a238c70SJohn Marino                   break;
12334a238c70SJohn Marino                 case MPFR_RNDD:
12344a238c70SJohn Marino                   round_away = MPFR_IS_NEG (p);
12354a238c70SJohn Marino                   break;
12364a238c70SJohn Marino                 case MPFR_RNDU:
12374a238c70SJohn Marino                   round_away = MPFR_IS_POS (p);
12384a238c70SJohn Marino                   break;
12394a238c70SJohn Marino                 case MPFR_RNDN:
12404a238c70SJohn Marino                   {
12414a238c70SJohn Marino                     /* compare |p| to y = 0.5*10^(-spec.prec) */
12424a238c70SJohn Marino                     mpfr_t y;
12434a238c70SJohn Marino                     mpfr_exp_t e = MAX (MPFR_PREC (p), 56);
12444a238c70SJohn Marino                     mpfr_init2 (y, e + 8);
12454a238c70SJohn Marino                     do
12464a238c70SJohn Marino                       {
12474a238c70SJohn Marino                         /* find a lower approximation of
12484a238c70SJohn Marino                            0.5*10^(-spec.prec) different from |p| */
12494a238c70SJohn Marino                         e += 8;
12504a238c70SJohn Marino                         mpfr_set_prec (y, e);
12514a238c70SJohn Marino                         mpfr_set_si (y, -spec.prec, MPFR_RNDN);
12524a238c70SJohn Marino                         mpfr_exp10 (y, y, MPFR_RNDD);
12534a238c70SJohn Marino                         mpfr_div_2ui (y, y, 1, MPFR_RNDN);
12544a238c70SJohn Marino                       } while (mpfr_cmpabs (y, p) == 0);
12554a238c70SJohn Marino 
12564a238c70SJohn Marino                     round_away = mpfr_cmpabs (y, p) < 0;
12574a238c70SJohn Marino                     mpfr_clear (y);
12584a238c70SJohn Marino                   }
12594a238c70SJohn Marino                   break;
12604a238c70SJohn Marino                 default:
12614a238c70SJohn Marino                   round_away = 0;
12624a238c70SJohn Marino                 }
12634a238c70SJohn Marino 
12644a238c70SJohn Marino               if (round_away)
12654a238c70SJohn Marino                 /* round away from zero: the last output digit is '1' */
12664a238c70SJohn Marino                 {
12674a238c70SJohn Marino                   np->fp_leading_zeros = spec.prec - 1;
12684a238c70SJohn Marino 
12694a238c70SJohn Marino                   np->fp_size = 1;
12704a238c70SJohn Marino                   str =
12714a238c70SJohn Marino                     (char *) (*__gmp_allocate_func) (1 + np->fp_size);
12724a238c70SJohn Marino                   str[0] = '1';
12734a238c70SJohn Marino                   str[1] = '\0';
12744a238c70SJohn Marino                   np->fp_ptr = register_string (np->sl, str);
12754a238c70SJohn Marino                 }
12764a238c70SJohn Marino               else
12774a238c70SJohn Marino                 /* only zeros in fractional part */
12784a238c70SJohn Marino                 {
12794a238c70SJohn Marino                   MPFR_ASSERTD (!spec_g);
12804a238c70SJohn Marino                   np->fp_leading_zeros = spec.prec;
12814a238c70SJohn Marino                 }
12824a238c70SJohn Marino             }
12834a238c70SJohn Marino           else
12844a238c70SJohn Marino             /* the most significant digits are the last
12854a238c70SJohn Marino                spec.prec + exp + 1 digits in fractional part */
12864a238c70SJohn Marino             {
12874a238c70SJohn Marino               char *ptr;
12884a238c70SJohn Marino               size_t str_len;
12894a238c70SJohn Marino               if (dec_info == NULL)
12904a238c70SJohn Marino                 {
12914a238c70SJohn Marino                   size_t nsd = spec.prec + exp + 1;
12924a238c70SJohn Marino                   /* WARNING: nsd may equal 1, but here we use the
12934a238c70SJohn Marino                      fact that mpfr_get_str can return one digit with
12944a238c70SJohn Marino                      base ten (undocumented feature, see comments in
12954a238c70SJohn Marino                      get_str.c) */
12964a238c70SJohn Marino 
12974a238c70SJohn Marino                   str = mpfr_get_str (NULL, &exp, 10, nsd, p, spec.rnd_mode);
12984a238c70SJohn Marino                   register_string (np->sl, str);
12994a238c70SJohn Marino                 }
13004a238c70SJohn Marino               else
13014a238c70SJohn Marino                 {
13024a238c70SJohn Marino                   exp = dec_info->exp;
13034a238c70SJohn Marino                   str = dec_info->str;
13044a238c70SJohn Marino                 }
13054a238c70SJohn Marino               if (MPFR_IS_NEG (p))
1306ab6d115fSJohn Marino                 /* skip sign */
13074a238c70SJohn Marino                 ++str;
13084a238c70SJohn Marino               if (exp == 1)
13094a238c70SJohn Marino                 /* round up to 1 */
13104a238c70SJohn Marino                 {
13114a238c70SJohn Marino                   MPFR_ASSERTD (str[0] == '1');
13124a238c70SJohn Marino                   np->ip_ptr[0] = '1';
13134a238c70SJohn Marino                   if (!spec_g || spec.alt)
13144a238c70SJohn Marino                     np->fp_leading_zeros = spec.prec;
13154a238c70SJohn Marino                 }
13164a238c70SJohn Marino               else
13174a238c70SJohn Marino                 {
13184a238c70SJohn Marino                   np->fp_ptr = str;
13194a238c70SJohn Marino                   np->fp_leading_zeros = -exp;
13204a238c70SJohn Marino                   MPFR_ASSERTD (exp <= 0);
13214a238c70SJohn Marino 
13224a238c70SJohn Marino                   str_len = strlen (str); /* the sign has been skipped */
13234a238c70SJohn Marino                   ptr = str + str_len - 1; /* points to the end of str */
13244a238c70SJohn Marino 
13254a238c70SJohn Marino                   if (!keep_trailing_zeros)
13264a238c70SJohn Marino                     /* remove trailing zeros, if any */
13274a238c70SJohn Marino                     {
13284a238c70SJohn Marino                       while ((*ptr == '0') && str_len)
13294a238c70SJohn Marino                         {
13304a238c70SJohn Marino                           --ptr;
13314a238c70SJohn Marino                           --str_len;
13324a238c70SJohn Marino                         }
13334a238c70SJohn Marino                     }
13344a238c70SJohn Marino 
13354a238c70SJohn Marino                   if (str_len > INT_MAX)
13364a238c70SJohn Marino                     /* too many digits in fractional part */
13374a238c70SJohn Marino                     return -1;
13384a238c70SJohn Marino 
13394a238c70SJohn Marino                   MPFR_ASSERTD (str_len > 0);
13404a238c70SJohn Marino                   np->fp_size = str_len;
13414a238c70SJohn Marino 
13424a238c70SJohn Marino                   if ((!spec_g || spec.alt)
13434a238c70SJohn Marino                       && spec.prec > 0
13444a238c70SJohn Marino                       && (np->fp_leading_zeros + np->fp_size < spec.prec))
13454a238c70SJohn Marino                     /* add missing trailing zeros */
13464a238c70SJohn Marino                     np->fp_trailing_zeros = spec.prec - np->fp_leading_zeros
13474a238c70SJohn Marino                       - np->fp_size;
13484a238c70SJohn Marino                 }
13494a238c70SJohn Marino             }
13504a238c70SJohn Marino         }
13514a238c70SJohn Marino 
13524a238c70SJohn Marino       if (spec.alt || np->fp_leading_zeros != 0 || np->fp_size != 0
13534a238c70SJohn Marino           || np->fp_trailing_zeros != 0)
13544a238c70SJohn Marino         np->point = MPFR_DECIMAL_POINT;
13554a238c70SJohn Marino     }
13564a238c70SJohn Marino   else
13574a238c70SJohn Marino     /* 1 <= |p| */
13584a238c70SJohn Marino     {
1359ab6d115fSJohn Marino       size_t str_len;
13604a238c70SJohn Marino 
13614a238c70SJohn Marino       /* Determine the position of the most significant decimal digit. */
13624a238c70SJohn Marino       exp = floor_log10 (p);
13634a238c70SJohn Marino       MPFR_ASSERTD (exp >= 0);
13644a238c70SJohn Marino       if (exp > INT_MAX)
13654a238c70SJohn Marino         /* P is too large to print all its integral part digits */
13664a238c70SJohn Marino         return -1;
13674a238c70SJohn Marino 
13684a238c70SJohn Marino       if (dec_info == NULL)
1369ab6d115fSJohn Marino         { /* this case occurs with mpfr_printf ("%.0RUf", x) with x=9.5 */
1370ab6d115fSJohn Marino           str =
1371ab6d115fSJohn Marino             mpfr_get_str (NULL, &exp, 10, spec.prec+exp+1, p, spec.rnd_mode);
13724a238c70SJohn Marino           register_string (np->sl, str);
13734a238c70SJohn Marino         }
13744a238c70SJohn Marino       else
13754a238c70SJohn Marino         {
13764a238c70SJohn Marino           exp = dec_info->exp;
13774a238c70SJohn Marino           str = dec_info->str;
13784a238c70SJohn Marino         }
13794a238c70SJohn Marino       np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign */
1380ab6d115fSJohn Marino       str_len = strlen (str);
1381ab6d115fSJohn Marino 
1382ab6d115fSJohn Marino       /* integral part */
1383ab6d115fSJohn Marino       if (exp > str_len)
1384ab6d115fSJohn Marino         /* mpfr_get_str gives no trailing zero when p is rounded up to the next
1385ab6d115fSJohn Marino            power of 10 (p integer, so no fractional part) */
1386ab6d115fSJohn Marino         {
1387ab6d115fSJohn Marino           np->ip_trailing_zeros = exp - str_len;
1388ab6d115fSJohn Marino           np->ip_size = str_len;
1389ab6d115fSJohn Marino         }
1390ab6d115fSJohn Marino       else
1391ab6d115fSJohn Marino         np->ip_size = exp;
13924a238c70SJohn Marino 
13934a238c70SJohn Marino       if (spec.group)
13944a238c70SJohn Marino         /* thousands separator in integral part */
13954a238c70SJohn Marino         np->thousands_sep = MPFR_THOUSANDS_SEPARATOR;
13964a238c70SJohn Marino 
1397ab6d115fSJohn Marino       /* fractional part */
1398ab6d115fSJohn Marino       str += np->ip_size;
1399ab6d115fSJohn Marino       str_len -= np->ip_size;
14004a238c70SJohn Marino       if (!keep_trailing_zeros)
14014a238c70SJohn Marino         /* remove trailing zeros, if any */
14024a238c70SJohn Marino         {
1403ab6d115fSJohn Marino           char *ptr = str + str_len - 1; /* pointer to the last digit of
1404ab6d115fSJohn Marino                                             str */
14054a238c70SJohn Marino           while ((*ptr == '0') && (str_len != 0))
14064a238c70SJohn Marino             {
14074a238c70SJohn Marino               --ptr;
14084a238c70SJohn Marino               --str_len;
14094a238c70SJohn Marino             }
14104a238c70SJohn Marino         }
14114a238c70SJohn Marino 
1412ab6d115fSJohn Marino       if (str_len > 0)
1413ab6d115fSJohn Marino         /* some nonzero digits in fractional part */
1414ab6d115fSJohn Marino         {
14154a238c70SJohn Marino           if (str_len > INT_MAX)
14164a238c70SJohn Marino             /* too many digits in fractional part */
14174a238c70SJohn Marino             return -1;
14184a238c70SJohn Marino 
14194a238c70SJohn Marino           np->point = MPFR_DECIMAL_POINT;
1420ab6d115fSJohn Marino           np->fp_ptr = str;
14214a238c70SJohn Marino           np->fp_size = str_len;
14224a238c70SJohn Marino         }
14234a238c70SJohn Marino 
1424ab6d115fSJohn Marino       if (keep_trailing_zeros && str_len < spec.prec)
1425ab6d115fSJohn Marino         /* add missing trailing zeros */
14264a238c70SJohn Marino         {
1427ab6d115fSJohn Marino           np->point = MPFR_DECIMAL_POINT;
1428ab6d115fSJohn Marino           np->fp_trailing_zeros = spec.prec - np->fp_size;
1429ab6d115fSJohn Marino         }
14304a238c70SJohn Marino 
1431ab6d115fSJohn Marino       if (spec.alt)
1432ab6d115fSJohn Marino         /* add decimal point even if no digits follow it */
14334a238c70SJohn Marino         np->point = MPFR_DECIMAL_POINT;
14344a238c70SJohn Marino     }
14354a238c70SJohn Marino 
14364a238c70SJohn Marino   return 0;
14374a238c70SJohn Marino }
14384a238c70SJohn Marino 
14394a238c70SJohn Marino /* partition_number determines the different parts of the string
14404a238c70SJohn Marino    representation of the number p according to the given specification.
14414a238c70SJohn Marino    partition_number initializes the given structure np, so all previous
14424a238c70SJohn Marino    information in that variable is lost.
14434a238c70SJohn Marino    return the total number of characters to be written.
14444a238c70SJohn Marino    return -1 if an error occured, in that case np's fields are in an undefined
14454a238c70SJohn Marino    state but all string buffers have been freed. */
14464a238c70SJohn Marino static int
partition_number(struct number_parts * np,mpfr_srcptr p,struct printf_spec spec)14474a238c70SJohn Marino partition_number (struct number_parts *np, mpfr_srcptr p,
14484a238c70SJohn Marino                   struct printf_spec spec)
14494a238c70SJohn Marino {
14504a238c70SJohn Marino   char *str;
14514a238c70SJohn Marino   long total;
14524a238c70SJohn Marino   int uppercase;
14534a238c70SJohn Marino 
14544a238c70SJohn Marino   /* WARNING: left justification means right space padding */
14554a238c70SJohn Marino   np->pad_type = spec.left ? RIGHT : spec.pad == '0' ? LEADING_ZEROS : LEFT;
14564a238c70SJohn Marino   np->pad_size = 0;
14574a238c70SJohn Marino   np->sign = '\0';
14584a238c70SJohn Marino   np->prefix_ptr =NULL;
14594a238c70SJohn Marino   np->prefix_size = 0;
14604a238c70SJohn Marino   np->thousands_sep = '\0';
14614a238c70SJohn Marino   np->ip_ptr = NULL;
14624a238c70SJohn Marino   np->ip_size = 0;
14634a238c70SJohn Marino   np->ip_trailing_zeros = 0;
14644a238c70SJohn Marino   np->point = '\0';
14654a238c70SJohn Marino   np->fp_leading_zeros = 0;
14664a238c70SJohn Marino   np->fp_ptr = NULL;
14674a238c70SJohn Marino   np->fp_size = 0;
14684a238c70SJohn Marino   np->fp_trailing_zeros = 0;
14694a238c70SJohn Marino   np->exp_ptr = NULL;
14704a238c70SJohn Marino   np->exp_size = 0;
14714a238c70SJohn Marino   np->sl = (struct string_list *)
14724a238c70SJohn Marino     (*__gmp_allocate_func) (sizeof (struct string_list));
14734a238c70SJohn Marino   init_string_list (np->sl);
14744a238c70SJohn Marino 
14754a238c70SJohn Marino   uppercase = spec.spec == 'A' || spec.spec == 'E' || spec.spec == 'F'
14764a238c70SJohn Marino     || spec.spec == 'G';
14774a238c70SJohn Marino 
14784a238c70SJohn Marino   if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (p)))
14794a238c70SJohn Marino     {
14804a238c70SJohn Marino       if (MPFR_IS_NAN (p))
14814a238c70SJohn Marino         {
14824a238c70SJohn Marino           if (np->pad_type == LEADING_ZEROS)
14834a238c70SJohn Marino             /* don't want "0000nan", change to right justification padding
14844a238c70SJohn Marino                with left spaces instead */
14854a238c70SJohn Marino             np->pad_type = LEFT;
14864a238c70SJohn Marino 
14874a238c70SJohn Marino           if (uppercase)
14884a238c70SJohn Marino             {
14894a238c70SJohn Marino               np->ip_size = MPFR_NAN_STRING_LENGTH;
14904a238c70SJohn Marino               str = (char *) (*__gmp_allocate_func) (1 + np->ip_size);
14914a238c70SJohn Marino               strcpy (str, MPFR_NAN_STRING_UC);
14924a238c70SJohn Marino               np->ip_ptr = register_string (np->sl, str);
14934a238c70SJohn Marino             }
14944a238c70SJohn Marino           else
14954a238c70SJohn Marino             {
14964a238c70SJohn Marino               np->ip_size = MPFR_NAN_STRING_LENGTH;
14974a238c70SJohn Marino               str = (char *) (*__gmp_allocate_func) (1 + np->ip_size);
14984a238c70SJohn Marino               strcpy (str, MPFR_NAN_STRING_LC);
14994a238c70SJohn Marino               np->ip_ptr = register_string (np->sl, str);
15004a238c70SJohn Marino             }
15014a238c70SJohn Marino         }
15024a238c70SJohn Marino       else if (MPFR_IS_INF (p))
15034a238c70SJohn Marino         {
15044a238c70SJohn Marino           if (np->pad_type == LEADING_ZEROS)
15054a238c70SJohn Marino             /* don't want "0000inf", change to right justification padding
15064a238c70SJohn Marino                with left spaces instead */
15074a238c70SJohn Marino             np->pad_type = LEFT;
15084a238c70SJohn Marino 
15094a238c70SJohn Marino           if (MPFR_IS_NEG (p))
15104a238c70SJohn Marino             np->sign = '-';
15114a238c70SJohn Marino 
15124a238c70SJohn Marino           if (uppercase)
15134a238c70SJohn Marino             {
15144a238c70SJohn Marino               np->ip_size = MPFR_INF_STRING_LENGTH;
15154a238c70SJohn Marino               str = (char *) (*__gmp_allocate_func) (1 + np->ip_size);
15164a238c70SJohn Marino               strcpy (str, MPFR_INF_STRING_UC);
15174a238c70SJohn Marino               np->ip_ptr = register_string (np->sl, str);
15184a238c70SJohn Marino             }
15194a238c70SJohn Marino           else
15204a238c70SJohn Marino             {
15214a238c70SJohn Marino               np->ip_size = MPFR_INF_STRING_LENGTH;
15224a238c70SJohn Marino               str = (char *) (*__gmp_allocate_func) (1 + np->ip_size);
15234a238c70SJohn Marino               strcpy (str, MPFR_INF_STRING_LC);
15244a238c70SJohn Marino               np->ip_ptr = register_string (np->sl, str);
15254a238c70SJohn Marino             }
15264a238c70SJohn Marino         }
15274a238c70SJohn Marino       else
15284a238c70SJohn Marino         /* p == 0 */
15294a238c70SJohn Marino         {
15304a238c70SJohn Marino           /* note: for 'g' spec, zero is always displayed with 'f'-style with
15314a238c70SJohn Marino              precision spec.prec - 1 and the trailing zeros are removed unless
15324a238c70SJohn Marino              the flag '#' is used. */
15334a238c70SJohn Marino           if (MPFR_IS_NEG (p))
15344a238c70SJohn Marino             /* signed zero */
15354a238c70SJohn Marino             np->sign = '-';
15364a238c70SJohn Marino           else if (spec.showsign || spec.space)
15374a238c70SJohn Marino             np->sign = spec.showsign ? '+' : ' ';
15384a238c70SJohn Marino 
15394a238c70SJohn Marino           if (spec.spec == 'a' || spec.spec == 'A')
15404a238c70SJohn Marino             /* prefix part */
15414a238c70SJohn Marino             {
15424a238c70SJohn Marino               np->prefix_size = 2;
15434a238c70SJohn Marino               str = (char *) (*__gmp_allocate_func) (1 + np->prefix_size);
15444a238c70SJohn Marino               str[0] = '0';
15454a238c70SJohn Marino               str[1] = uppercase ? 'X' : 'x';
15464a238c70SJohn Marino               str[2] = '\0';
15474a238c70SJohn Marino               np->prefix_ptr = register_string (np->sl, str);
15484a238c70SJohn Marino             }
15494a238c70SJohn Marino 
15504a238c70SJohn Marino           /* integral part */
15514a238c70SJohn Marino           np->ip_size = 1;
15524a238c70SJohn Marino           str = (char *) (*__gmp_allocate_func) (1 + np->ip_size);
15534a238c70SJohn Marino           str[0] = '0';
15544a238c70SJohn Marino           str[1] = '\0';
15554a238c70SJohn Marino           np->ip_ptr = register_string (np->sl, str);
15564a238c70SJohn Marino 
15574a238c70SJohn Marino           if (spec.prec > 0
15584a238c70SJohn Marino               && ((spec.spec != 'g' && spec.spec != 'G') || spec.alt))
15594a238c70SJohn Marino             /* fractional part */
15604a238c70SJohn Marino             {
15614a238c70SJohn Marino               np->point = MPFR_DECIMAL_POINT;
1562*122b686eSSascha Wildner               np->fp_trailing_zeros = (spec.spec == 'g' || spec.spec == 'G') ?
15634a238c70SJohn Marino                 spec.prec - 1 : spec.prec;
15644a238c70SJohn Marino             }
15654a238c70SJohn Marino           else if (spec.alt)
15664a238c70SJohn Marino             np->point = MPFR_DECIMAL_POINT;
15674a238c70SJohn Marino 
15684a238c70SJohn Marino           if (spec.spec == 'a' || spec.spec == 'A' || spec.spec == 'b'
15694a238c70SJohn Marino               || spec.spec == 'e' || spec.spec == 'E')
15704a238c70SJohn Marino             /* exponent part */
15714a238c70SJohn Marino             {
15724a238c70SJohn Marino               np->exp_size = (spec.spec == 'e' || spec.spec == 'E') ? 4 : 3;
15734a238c70SJohn Marino               str = (char *) (*__gmp_allocate_func) (1 + np->exp_size);
15744a238c70SJohn Marino               if (spec.spec == 'e' || spec.spec == 'E')
15754a238c70SJohn Marino                 strcpy (str, uppercase ? "E+00" : "e+00");
15764a238c70SJohn Marino               else
15774a238c70SJohn Marino                 strcpy (str, uppercase ? "P+0" : "p+0");
15784a238c70SJohn Marino               np->exp_ptr = register_string (np->sl, str);
15794a238c70SJohn Marino             }
15804a238c70SJohn Marino         }
15814a238c70SJohn Marino     }
15824a238c70SJohn Marino   else
15834a238c70SJohn Marino     /* regular p, p != 0 */
15844a238c70SJohn Marino     {
15854a238c70SJohn Marino       if (spec.spec == 'a' || spec.spec == 'A' || spec.spec == 'b')
15864a238c70SJohn Marino         {
15874a238c70SJohn Marino           if (regular_ab (np, p, spec) == -1)
15884a238c70SJohn Marino             goto error;
15894a238c70SJohn Marino         }
15904a238c70SJohn Marino       else if (spec.spec == 'f' || spec.spec == 'F')
15914a238c70SJohn Marino         {
15924a238c70SJohn Marino           if (spec.prec == -1)
15934a238c70SJohn Marino             spec.prec = 6;
15944a238c70SJohn Marino           if (regular_fg (np, p, spec, NULL) == -1)
15954a238c70SJohn Marino             goto error;
15964a238c70SJohn Marino         }
15974a238c70SJohn Marino       else if (spec.spec == 'e' || spec.spec == 'E')
15984a238c70SJohn Marino         {
15994a238c70SJohn Marino           if (regular_eg (np, p, spec, NULL) == -1)
16004a238c70SJohn Marino             goto error;
16014a238c70SJohn Marino         }
16024a238c70SJohn Marino       else
16034a238c70SJohn Marino         /* %g case */
16044a238c70SJohn Marino         {
16054a238c70SJohn Marino           /* Use the C99 rules:
16064a238c70SJohn Marino              if T > X >= -4 then the conversion is with style 'f'/'F' and
16074a238c70SJohn Marino              precision T-(X+1).
16084a238c70SJohn Marino              otherwise, the conversion is with style 'e'/'E' and
16094a238c70SJohn Marino              precision T-1.
16104a238c70SJohn Marino              where T is the threshold computed below and X is the exponent
16114a238c70SJohn Marino              that would be displayed with style 'e' and precision T-1. */
16124a238c70SJohn Marino           int threshold;
16134a238c70SJohn Marino           mpfr_exp_t x;
16144a238c70SJohn Marino           struct decimal_info dec_info;
16154a238c70SJohn Marino 
16164a238c70SJohn Marino           threshold = (spec.prec < 0) ? 6 : (spec.prec == 0) ? 1 : spec.prec;
16174a238c70SJohn Marino           dec_info.str = mpfr_get_str (NULL, &dec_info.exp, 10, threshold,
16184a238c70SJohn Marino                                         p, spec.rnd_mode);
16194a238c70SJohn Marino           register_string (np->sl, dec_info.str);
16204a238c70SJohn Marino           /* mpfr_get_str corresponds to a significand between 0.1 and 1,
16214a238c70SJohn Marino              whereas here we want a significand between 1 and 10. */
16224a238c70SJohn Marino           x = dec_info.exp - 1;
16234a238c70SJohn Marino 
16244a238c70SJohn Marino           if (threshold > x && x >= -4)
16254a238c70SJohn Marino             {
16264a238c70SJohn Marino               /* the conversion is with style 'f' */
16274a238c70SJohn Marino               spec.prec = threshold - x - 1;
16284a238c70SJohn Marino 
16294a238c70SJohn Marino               if (regular_fg (np, p, spec, &dec_info) == -1)
16304a238c70SJohn Marino                 goto error;
16314a238c70SJohn Marino             }
16324a238c70SJohn Marino           else
16334a238c70SJohn Marino             {
16344a238c70SJohn Marino               spec.prec = threshold - 1;
16354a238c70SJohn Marino 
16364a238c70SJohn Marino               if (regular_eg (np, p, spec, &dec_info) == -1)
16374a238c70SJohn Marino                 goto error;
16384a238c70SJohn Marino             }
16394a238c70SJohn Marino         }
16404a238c70SJohn Marino     }
16414a238c70SJohn Marino 
16424a238c70SJohn Marino   /* compute the number of characters to be written verifying it is not too
16434a238c70SJohn Marino      much */
16444a238c70SJohn Marino   total = np->sign ? 1 : 0;
16454a238c70SJohn Marino   total += np->prefix_size;
16464a238c70SJohn Marino   total += np->ip_size;
16474a238c70SJohn Marino   if (MPFR_UNLIKELY (total < 0 || total > INT_MAX))
16484a238c70SJohn Marino     goto error;
16494a238c70SJohn Marino   total += np->ip_trailing_zeros;
16504a238c70SJohn Marino   if (MPFR_UNLIKELY (total < 0 || total > INT_MAX))
16514a238c70SJohn Marino     goto error;
16524a238c70SJohn Marino   if (np->thousands_sep)
16534a238c70SJohn Marino     /* ' flag, style f and the thousands separator in current locale is not
16544a238c70SJohn Marino        reduced to the null character */
16554a238c70SJohn Marino     total += (np->ip_size + np->ip_trailing_zeros) / 3;
16564a238c70SJohn Marino   if (MPFR_UNLIKELY (total < 0 || total > INT_MAX))
16574a238c70SJohn Marino     goto error;
16584a238c70SJohn Marino   if (np->point)
16594a238c70SJohn Marino     ++total;
16604a238c70SJohn Marino   total += np->fp_leading_zeros;
16614a238c70SJohn Marino   if (MPFR_UNLIKELY (total < 0 || total > INT_MAX))
16624a238c70SJohn Marino     goto error;
16634a238c70SJohn Marino   total += np->fp_size;
16644a238c70SJohn Marino   if (MPFR_UNLIKELY (total < 0 || total > INT_MAX))
16654a238c70SJohn Marino     goto error;
16664a238c70SJohn Marino   total += np->fp_trailing_zeros;
16674a238c70SJohn Marino   if (MPFR_UNLIKELY (total < 0 || total > INT_MAX))
16684a238c70SJohn Marino     goto error;
16694a238c70SJohn Marino   total += np->exp_size;
16704a238c70SJohn Marino   if (MPFR_UNLIKELY (total < 0 || total > INT_MAX))
16714a238c70SJohn Marino     goto error;
16724a238c70SJohn Marino 
16734a238c70SJohn Marino   if (spec.width > total)
16744a238c70SJohn Marino     /* pad with spaces or zeros depending on np->pad_type */
16754a238c70SJohn Marino     {
16764a238c70SJohn Marino       np->pad_size = spec.width - total;
16774a238c70SJohn Marino       total += np->pad_size; /* here total == spec.width,
16784a238c70SJohn Marino                                 so 0 < total < INT_MAX */
16794a238c70SJohn Marino     }
16804a238c70SJohn Marino 
16814a238c70SJohn Marino   return total;
16824a238c70SJohn Marino 
16834a238c70SJohn Marino  error:
16844a238c70SJohn Marino   clear_string_list (np->sl);
16854a238c70SJohn Marino   np->prefix_ptr = NULL;
16864a238c70SJohn Marino   np->ip_ptr = NULL;
16874a238c70SJohn Marino   np->fp_ptr = NULL;
16884a238c70SJohn Marino   np->exp_ptr = NULL;
16894a238c70SJohn Marino   return -1;
16904a238c70SJohn Marino }
16914a238c70SJohn Marino 
16924a238c70SJohn Marino /* sprnt_fp prints a mpfr_t according to spec.spec specification.
16934a238c70SJohn Marino 
16944a238c70SJohn Marino    return the size of the string (not counting the terminating '\0')
16954a238c70SJohn Marino    return -1 if the built string is too long (i.e. has more than
16964a238c70SJohn Marino    INT_MAX characters). */
16974a238c70SJohn Marino static int
sprnt_fp(struct string_buffer * buf,mpfr_srcptr p,const struct printf_spec spec)16984a238c70SJohn Marino sprnt_fp (struct string_buffer *buf, mpfr_srcptr p,
16994a238c70SJohn Marino           const struct printf_spec spec)
17004a238c70SJohn Marino {
17014a238c70SJohn Marino   int length;
17024a238c70SJohn Marino   struct number_parts np;
17034a238c70SJohn Marino 
17044a238c70SJohn Marino   length = partition_number (&np, p, spec);
17054a238c70SJohn Marino   if (length < 0)
17064a238c70SJohn Marino     return -1;
17074a238c70SJohn Marino 
17084a238c70SJohn Marino   /* right justification padding with left spaces */
17094a238c70SJohn Marino   if (np.pad_type == LEFT && np.pad_size != 0)
17104a238c70SJohn Marino     buffer_pad (buf, ' ', np.pad_size);
17114a238c70SJohn Marino 
17124a238c70SJohn Marino   /* sign character (may be '-', '+', or ' ') */
17134a238c70SJohn Marino   if (np.sign)
17144a238c70SJohn Marino     buffer_pad (buf, np.sign, 1);
17154a238c70SJohn Marino 
17164a238c70SJohn Marino   /* prefix part */
17174a238c70SJohn Marino   if (np.prefix_ptr)
17184a238c70SJohn Marino     buffer_cat (buf, np.prefix_ptr, np.prefix_size);
17194a238c70SJohn Marino 
17204a238c70SJohn Marino   /* right justification  padding with leading zeros */
17214a238c70SJohn Marino   if (np.pad_type == LEADING_ZEROS && np.pad_size != 0)
17224a238c70SJohn Marino     buffer_pad (buf, '0', np.pad_size);
17234a238c70SJohn Marino 
17244a238c70SJohn Marino   /* integral part (may also be "nan" or "inf") */
17254a238c70SJohn Marino   MPFR_ASSERTN (np.ip_ptr != NULL); /* never empty */
17264a238c70SJohn Marino   if (MPFR_UNLIKELY (np.thousands_sep))
17274a238c70SJohn Marino     buffer_sandwich (buf, np.ip_ptr, np.ip_size, np.ip_trailing_zeros,
17284a238c70SJohn Marino                      np.thousands_sep);
17294a238c70SJohn Marino   else
17304a238c70SJohn Marino     {
17314a238c70SJohn Marino       buffer_cat (buf, np.ip_ptr, np.ip_size);
17324a238c70SJohn Marino 
17334a238c70SJohn Marino       /* trailing zeros in integral part */
17344a238c70SJohn Marino       if (np.ip_trailing_zeros != 0)
17354a238c70SJohn Marino         buffer_pad (buf, '0', np.ip_trailing_zeros);
17364a238c70SJohn Marino     }
17374a238c70SJohn Marino 
17384a238c70SJohn Marino   /* decimal point */
17394a238c70SJohn Marino   if (np.point)
17404a238c70SJohn Marino     buffer_pad (buf, np.point, 1);
17414a238c70SJohn Marino 
17424a238c70SJohn Marino   /* leading zeros in fractional part */
17434a238c70SJohn Marino   if (np.fp_leading_zeros != 0)
17444a238c70SJohn Marino     buffer_pad (buf, '0', np.fp_leading_zeros);
17454a238c70SJohn Marino 
17464a238c70SJohn Marino   /* significant digits in fractional part */
17474a238c70SJohn Marino   if (np.fp_ptr)
17484a238c70SJohn Marino     buffer_cat (buf, np.fp_ptr, np.fp_size);
17494a238c70SJohn Marino 
17504a238c70SJohn Marino   /* trailing zeros in fractional part */
17514a238c70SJohn Marino   if (np.fp_trailing_zeros != 0)
17524a238c70SJohn Marino     buffer_pad (buf, '0', np.fp_trailing_zeros);
17534a238c70SJohn Marino 
17544a238c70SJohn Marino   /* exponent part */
17554a238c70SJohn Marino   if (np.exp_ptr)
17564a238c70SJohn Marino     buffer_cat (buf, np.exp_ptr, np.exp_size);
17574a238c70SJohn Marino 
17584a238c70SJohn Marino   /* left justication padding with right spaces */
17594a238c70SJohn Marino   if (np.pad_type == RIGHT && np.pad_size != 0)
17604a238c70SJohn Marino     buffer_pad (buf, ' ', np.pad_size);
17614a238c70SJohn Marino 
17624a238c70SJohn Marino   clear_string_list (np.sl);
17634a238c70SJohn Marino   return length;
17644a238c70SJohn Marino }
17654a238c70SJohn Marino 
17664a238c70SJohn Marino int
mpfr_vasprintf(char ** ptr,const char * fmt,va_list ap)17674a238c70SJohn Marino mpfr_vasprintf (char **ptr, const char *fmt, va_list ap)
17684a238c70SJohn Marino {
17694a238c70SJohn Marino   struct string_buffer buf;
17704a238c70SJohn Marino   size_t nbchar;
17714a238c70SJohn Marino 
17724a238c70SJohn Marino   /* informations on the conversion specification filled by the parser */
17734a238c70SJohn Marino   struct printf_spec spec;
17744a238c70SJohn Marino   /* flag raised when previous part of fmt need to be processed by
17754a238c70SJohn Marino      gmp_vsnprintf */
17764a238c70SJohn Marino   int xgmp_fmt_flag;
17774a238c70SJohn Marino   /* beginning and end of the previous unprocessed part of fmt */
17784a238c70SJohn Marino   const char *start, *end;
17794a238c70SJohn Marino   /* pointer to arguments for gmp_vasprintf */
17804a238c70SJohn Marino   va_list ap2;
17814a238c70SJohn Marino 
17824a238c70SJohn Marino   MPFR_SAVE_EXPO_DECL (expo);
17834a238c70SJohn Marino   MPFR_SAVE_EXPO_MARK (expo);
17844a238c70SJohn Marino 
17854a238c70SJohn Marino   nbchar = 0;
17864a238c70SJohn Marino   buffer_init (&buf, 4096);
17874a238c70SJohn Marino   xgmp_fmt_flag = 0;
17884a238c70SJohn Marino   va_copy (ap2, ap);
17894a238c70SJohn Marino   start = fmt;
17904a238c70SJohn Marino   while (*fmt)
17914a238c70SJohn Marino     {
17924a238c70SJohn Marino       /* Look for the next format specification */
17934a238c70SJohn Marino       while ((*fmt) && (*fmt != '%'))
17944a238c70SJohn Marino         ++fmt;
17954a238c70SJohn Marino 
17964a238c70SJohn Marino       if (*fmt == '\0')
17974a238c70SJohn Marino         break;
17984a238c70SJohn Marino 
17994a238c70SJohn Marino       if (*++fmt == '%')
18004a238c70SJohn Marino         /* %%: go one step further otherwise the second '%' would be
18014a238c70SJohn Marino            considered as a new conversion specification introducing
18024a238c70SJohn Marino            character */
18034a238c70SJohn Marino         {
18044a238c70SJohn Marino           ++fmt;
18054a238c70SJohn Marino           xgmp_fmt_flag = 1;
18064a238c70SJohn Marino           continue;
18074a238c70SJohn Marino         }
18084a238c70SJohn Marino 
18094a238c70SJohn Marino       end = fmt - 1;
18104a238c70SJohn Marino 
18114a238c70SJohn Marino       /* format string analysis */
18124a238c70SJohn Marino       specinfo_init (&spec);
18134a238c70SJohn Marino       fmt = parse_flags (fmt, &spec);
18144a238c70SJohn Marino 
18154a238c70SJohn Marino       READ_INT (ap, fmt, spec, width, width_analysis);
18164a238c70SJohn Marino     width_analysis:
18174a238c70SJohn Marino       if (spec.width < 0)
18184a238c70SJohn Marino         {
18194a238c70SJohn Marino           spec.left = 1;
18204a238c70SJohn Marino           spec.width = -spec.width;
18214a238c70SJohn Marino           MPFR_ASSERTN (spec.width < INT_MAX);
18224a238c70SJohn Marino         }
18234a238c70SJohn Marino       if (*fmt == '.')
18244a238c70SJohn Marino         {
18254a238c70SJohn Marino           const char *f = ++fmt;
18264a238c70SJohn Marino           READ_INT (ap, fmt, spec, prec, prec_analysis);
18274a238c70SJohn Marino         prec_analysis:
18284a238c70SJohn Marino           if (f == fmt)
18294a238c70SJohn Marino             spec.prec = -1;
18304a238c70SJohn Marino         }
18314a238c70SJohn Marino       else
18324a238c70SJohn Marino         spec.prec = -1;
18334a238c70SJohn Marino 
18344a238c70SJohn Marino       fmt = parse_arg_type (fmt, &spec);
18354a238c70SJohn Marino       if (spec.arg_type == UNSUPPORTED)
18364a238c70SJohn Marino         /* the current architecture doesn't support this type */
18374a238c70SJohn Marino         {
18384a238c70SJohn Marino           goto error;
18394a238c70SJohn Marino         }
18404a238c70SJohn Marino       else if (spec.arg_type == MPFR_ARG)
18414a238c70SJohn Marino         {
18424a238c70SJohn Marino           switch (*fmt)
18434a238c70SJohn Marino             {
18444a238c70SJohn Marino             case '\0':
18454a238c70SJohn Marino               break;
18464a238c70SJohn Marino             case '*':
18474a238c70SJohn Marino               ++fmt;
18484a238c70SJohn Marino               spec.rnd_mode = (mpfr_rnd_t) va_arg (ap, int);
18494a238c70SJohn Marino               break;
18504a238c70SJohn Marino             case 'D':
18514a238c70SJohn Marino               ++fmt;
18524a238c70SJohn Marino               spec.rnd_mode = MPFR_RNDD;
18534a238c70SJohn Marino               break;
18544a238c70SJohn Marino             case 'U':
18554a238c70SJohn Marino               ++fmt;
18564a238c70SJohn Marino               spec.rnd_mode = MPFR_RNDU;
18574a238c70SJohn Marino               break;
18584a238c70SJohn Marino             case 'Y':
18594a238c70SJohn Marino               ++fmt;
18604a238c70SJohn Marino               spec.rnd_mode = MPFR_RNDA;
18614a238c70SJohn Marino               break;
18624a238c70SJohn Marino             case 'Z':
18634a238c70SJohn Marino               ++fmt;
18644a238c70SJohn Marino               spec.rnd_mode = MPFR_RNDZ;
18654a238c70SJohn Marino               break;
18664a238c70SJohn Marino             case 'N':
18674a238c70SJohn Marino               ++fmt;
18684a238c70SJohn Marino             default:
18694a238c70SJohn Marino               spec.rnd_mode = MPFR_RNDN;
18704a238c70SJohn Marino             }
18714a238c70SJohn Marino         }
18724a238c70SJohn Marino 
18734a238c70SJohn Marino       spec.spec = *fmt;
18744a238c70SJohn Marino       if (!specinfo_is_valid (spec))
18754a238c70SJohn Marino         goto error;
18764a238c70SJohn Marino 
18774a238c70SJohn Marino       if (*fmt)
18784a238c70SJohn Marino         fmt++;
18794a238c70SJohn Marino 
18804a238c70SJohn Marino       /* Format processing */
18814a238c70SJohn Marino       if (spec.spec == '\0')
18824a238c70SJohn Marino         /* end of the format string */
18834a238c70SJohn Marino         break;
18844a238c70SJohn Marino       else if (spec.spec == 'n')
18854a238c70SJohn Marino         /* put the number of characters written so far in the location pointed
18864a238c70SJohn Marino            by the next va_list argument; the types of pointer accepted are the
18874a238c70SJohn Marino            same as in GMP (except unsupported quad_t) plus pointer to a mpfr_t
18884a238c70SJohn Marino            so as to be able to accept the same format strings. */
18894a238c70SJohn Marino         {
18904a238c70SJohn Marino           void *p;
18914a238c70SJohn Marino           size_t nchar;
18924a238c70SJohn Marino 
18934a238c70SJohn Marino           p = va_arg (ap, void *);
18944a238c70SJohn Marino           FLUSH (xgmp_fmt_flag, start, end, ap2, &buf);
18954a238c70SJohn Marino           va_end (ap2);
18964a238c70SJohn Marino           start = fmt;
18974a238c70SJohn Marino           nchar = buf.curr - buf.start;
18984a238c70SJohn Marino 
18994a238c70SJohn Marino           switch (spec.arg_type)
19004a238c70SJohn Marino             {
19014a238c70SJohn Marino             case CHAR_ARG:
19024a238c70SJohn Marino               *(char *) p = (char) nchar;
19034a238c70SJohn Marino               break;
19044a238c70SJohn Marino             case SHORT_ARG:
19054a238c70SJohn Marino               *(short *) p = (short) nchar;
19064a238c70SJohn Marino               break;
19074a238c70SJohn Marino             case LONG_ARG:
19084a238c70SJohn Marino               *(long *) p = (long) nchar;
19094a238c70SJohn Marino               break;
19104a238c70SJohn Marino #ifdef HAVE_LONG_LONG
19114a238c70SJohn Marino             case LONG_LONG_ARG:
19124a238c70SJohn Marino               *(long long *) p = (long long) nchar;
19134a238c70SJohn Marino               break;
19144a238c70SJohn Marino #endif
19154a238c70SJohn Marino #ifdef _MPFR_H_HAVE_INTMAX_T
19164a238c70SJohn Marino             case INTMAX_ARG:
19174a238c70SJohn Marino               *(intmax_t *) p = (intmax_t) nchar;
19184a238c70SJohn Marino               break;
19194a238c70SJohn Marino #endif
19204a238c70SJohn Marino             case SIZE_ARG:
19214a238c70SJohn Marino               *(size_t *) p = nchar;
19224a238c70SJohn Marino               break;
19234a238c70SJohn Marino             case PTRDIFF_ARG:
19244a238c70SJohn Marino               *(ptrdiff_t *) p = (ptrdiff_t) nchar;
19254a238c70SJohn Marino               break;
19264a238c70SJohn Marino             case MPF_ARG:
19274a238c70SJohn Marino               mpf_set_ui ((mpf_ptr) p, (unsigned long) nchar);
19284a238c70SJohn Marino               break;
19294a238c70SJohn Marino             case MPQ_ARG:
19304a238c70SJohn Marino               mpq_set_ui ((mpq_ptr) p, (unsigned long) nchar, 1L);
19314a238c70SJohn Marino               break;
19324a238c70SJohn Marino             case MP_LIMB_ARG:
19334a238c70SJohn Marino               *(mp_limb_t *) p = (mp_limb_t) nchar;
19344a238c70SJohn Marino               break;
19354a238c70SJohn Marino             case MP_LIMB_ARRAY_ARG:
19364a238c70SJohn Marino               {
19374a238c70SJohn Marino                 mp_limb_t *q = (mp_limb_t *) p;
19384a238c70SJohn Marino                 mp_size_t n;
19394a238c70SJohn Marino                 n = va_arg (ap, mp_size_t);
19404a238c70SJohn Marino                 if (n < 0)
19414a238c70SJohn Marino                   n = -n;
19424a238c70SJohn Marino                 else if (n == 0)
19434a238c70SJohn Marino                   break;
19444a238c70SJohn Marino 
19454a238c70SJohn Marino                 /* we assume here that mp_limb_t is wider than int */
19464a238c70SJohn Marino                 *q = (mp_limb_t) nchar;
19474a238c70SJohn Marino                 while (--n != 0)
19484a238c70SJohn Marino                   {
19494a238c70SJohn Marino                     q++;
19504a238c70SJohn Marino                     *q = (mp_limb_t) 0;
19514a238c70SJohn Marino                   }
19524a238c70SJohn Marino               }
19534a238c70SJohn Marino               break;
19544a238c70SJohn Marino             case MPZ_ARG:
19554a238c70SJohn Marino               mpz_set_ui ((mpz_ptr) p, (unsigned long) nchar);
19564a238c70SJohn Marino               break;
19574a238c70SJohn Marino 
19584a238c70SJohn Marino             case MPFR_ARG:
19594a238c70SJohn Marino               mpfr_set_ui ((mpfr_ptr) p, (unsigned long) nchar,
19604a238c70SJohn Marino                            spec.rnd_mode);
19614a238c70SJohn Marino               break;
19624a238c70SJohn Marino 
19634a238c70SJohn Marino             default:
19644a238c70SJohn Marino               *(int *) p = (int) nchar;
19654a238c70SJohn Marino             }
19664a238c70SJohn Marino           va_copy (ap2, ap); /* after the switch, due to MP_LIMB_ARRAY_ARG
19674a238c70SJohn Marino                                 case */
19684a238c70SJohn Marino         }
19694a238c70SJohn Marino       else if (spec.arg_type == MPFR_PREC_ARG)
19704a238c70SJohn Marino         /* output mpfr_prec_t variable */
19714a238c70SJohn Marino         {
19724a238c70SJohn Marino           char *s;
19734a238c70SJohn Marino           char format[MPFR_PREC_FORMAT_SIZE + 6]; /* see examples below */
19744a238c70SJohn Marino           size_t length;
19754a238c70SJohn Marino           mpfr_prec_t prec;
19764a238c70SJohn Marino           prec = va_arg (ap, mpfr_prec_t);
19774a238c70SJohn Marino 
19784a238c70SJohn Marino           FLUSH (xgmp_fmt_flag, start, end, ap2, &buf);
19794a238c70SJohn Marino           va_end (ap2);
19804a238c70SJohn Marino           va_copy (ap2, ap);
19814a238c70SJohn Marino           start = fmt;
19824a238c70SJohn Marino 
19834a238c70SJohn Marino           /* construct format string, like "%*.*hu" "%*.*u" or "%*.*lu" */
19844a238c70SJohn Marino           format[0] = '%';
19854a238c70SJohn Marino           format[1] = '*';
19864a238c70SJohn Marino           format[2] = '.';
19874a238c70SJohn Marino           format[3] = '*';
19884a238c70SJohn Marino           format[4] = '\0';
19894a238c70SJohn Marino           strcat (format, MPFR_PREC_FORMAT_TYPE);
19904a238c70SJohn Marino           format[4 + MPFR_PREC_FORMAT_SIZE] = spec.spec;
19914a238c70SJohn Marino           format[5 + MPFR_PREC_FORMAT_SIZE] = '\0';
19924a238c70SJohn Marino           length = gmp_asprintf (&s, format, spec.width, spec.prec, prec);
19934a238c70SJohn Marino           if (buf.size <= INT_MAX - length)
19944a238c70SJohn Marino             {
19954a238c70SJohn Marino               buffer_cat (&buf, s, length);
19964a238c70SJohn Marino               mpfr_free_str (s);
19974a238c70SJohn Marino             }
19984a238c70SJohn Marino           else
19994a238c70SJohn Marino             {
20004a238c70SJohn Marino               mpfr_free_str (s);
20014a238c70SJohn Marino               goto overflow_error;
20024a238c70SJohn Marino             }
20034a238c70SJohn Marino         }
20044a238c70SJohn Marino       else if (spec.arg_type == MPFR_ARG)
20054a238c70SJohn Marino         /* output a mpfr_t variable */
20064a238c70SJohn Marino         {
20074a238c70SJohn Marino           mpfr_srcptr p;
20084a238c70SJohn Marino 
20094a238c70SJohn Marino           p = va_arg (ap, mpfr_srcptr);
20104a238c70SJohn Marino 
20114a238c70SJohn Marino           FLUSH (xgmp_fmt_flag, start, end, ap2, &buf);
20124a238c70SJohn Marino           va_end (ap2);
20134a238c70SJohn Marino           va_copy (ap2, ap);
20144a238c70SJohn Marino           start = fmt;
20154a238c70SJohn Marino 
20164a238c70SJohn Marino           switch (spec.spec)
20174a238c70SJohn Marino             {
20184a238c70SJohn Marino             case 'a':
20194a238c70SJohn Marino             case 'A':
20204a238c70SJohn Marino             case 'b':
20214a238c70SJohn Marino             case 'e':
20224a238c70SJohn Marino             case 'E':
20234a238c70SJohn Marino             case 'f':
20244a238c70SJohn Marino             case 'F':
20254a238c70SJohn Marino             case 'g':
20264a238c70SJohn Marino             case 'G':
20274a238c70SJohn Marino               if (sprnt_fp (&buf, p, spec) < 0)
20284a238c70SJohn Marino                 goto overflow_error;
20294a238c70SJohn Marino               break;
20304a238c70SJohn Marino 
20314a238c70SJohn Marino             default:
20324a238c70SJohn Marino               /* unsupported specifier */
20334a238c70SJohn Marino               goto error;
20344a238c70SJohn Marino             }
20354a238c70SJohn Marino         }
20364a238c70SJohn Marino       else
20374a238c70SJohn Marino         /* gmp_printf specification, step forward in the va_list */
20384a238c70SJohn Marino         {
20394a238c70SJohn Marino           CONSUME_VA_ARG (spec, ap);
20404a238c70SJohn Marino           xgmp_fmt_flag = 1;
20414a238c70SJohn Marino         }
20424a238c70SJohn Marino     }
20434a238c70SJohn Marino 
20444a238c70SJohn Marino   if (start != fmt)
20454a238c70SJohn Marino     FLUSH (xgmp_fmt_flag, start, fmt, ap2, &buf);
20464a238c70SJohn Marino 
20474a238c70SJohn Marino   va_end (ap2);
20484a238c70SJohn Marino   nbchar = buf.curr - buf.start;
20494a238c70SJohn Marino   MPFR_ASSERTD (nbchar == strlen (buf.start));
20504a238c70SJohn Marino   buf.start =
20514a238c70SJohn Marino     (char *) (*__gmp_reallocate_func) (buf.start, buf.size, nbchar + 1);
20524a238c70SJohn Marino   buf.size = nbchar + 1; /* update needed for __gmp_free_func below when
20534a238c70SJohn Marino                             nbchar is too large (overflow_error) */
20544a238c70SJohn Marino   *ptr = buf.start;
20554a238c70SJohn Marino 
20564a238c70SJohn Marino   /* If nbchar is larger than INT_MAX, the ISO C99 standard is silent, but
20574a238c70SJohn Marino      POSIX says concerning the snprintf() function:
20584a238c70SJohn Marino      "[EOVERFLOW] The value of n is greater than {INT_MAX} or the
20594a238c70SJohn Marino      number of bytes needed to hold the output excluding the
20604a238c70SJohn Marino      terminating null is greater than {INT_MAX}." See:
20614a238c70SJohn Marino      http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html
20624a238c70SJohn Marino      But it doesn't say anything concerning the other printf-like functions.
20634a238c70SJohn Marino      A defect report has been submitted to austin-review-l (item 2532).
20644a238c70SJohn Marino      So, for the time being, we return a negative value and set the erange
20654a238c70SJohn Marino      flag, and set errno to EOVERFLOW in POSIX system. */
20664a238c70SJohn Marino   if (nbchar <= INT_MAX)
20674a238c70SJohn Marino     {
20684a238c70SJohn Marino       MPFR_SAVE_EXPO_FREE (expo);
20694a238c70SJohn Marino       return nbchar;
20704a238c70SJohn Marino     }
20714a238c70SJohn Marino 
20724a238c70SJohn Marino  overflow_error:
20734a238c70SJohn Marino   MPFR_SAVE_EXPO_UPDATE_FLAGS(expo, MPFR_FLAGS_ERANGE);
20744a238c70SJohn Marino #ifdef EOVERFLOW
20754a238c70SJohn Marino   errno = EOVERFLOW;
20764a238c70SJohn Marino #endif
20774a238c70SJohn Marino 
20784a238c70SJohn Marino  error:
20794a238c70SJohn Marino   MPFR_SAVE_EXPO_FREE (expo);
20804a238c70SJohn Marino   *ptr = NULL;
20814a238c70SJohn Marino   (*__gmp_free_func) (buf.start, buf.size);
20824a238c70SJohn Marino 
20834a238c70SJohn Marino   return -1;
20844a238c70SJohn Marino }
20854a238c70SJohn Marino 
20864a238c70SJohn Marino #endif /* HAVE_STDARG */
2087