| /* vsprintf with automatic memory allocation. | |
| Copyright (C) 1999, 2002-2026 Free Software Foundation, Inc. | |
| This file is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU Lesser General Public License as | |
| published by the Free Software Foundation; either version 2.1 of the | |
| License, or (at your option) any later version. | |
| This file is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| /* This file can be parametrized with the following macros: | |
| VASNPRINTF The name of the function being defined. | |
| FCHAR_T The element type of the format string. | |
| DCHAR_T The element type of the destination (result) string. | |
| FCHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters | |
| in the format string are ASCII. MUST be set if | |
| FCHAR_T and DCHAR_T are not the same type. | |
| DIRECTIVE Structure denoting a format directive. | |
| Depends on FCHAR_T. | |
| DIRECTIVES Structure denoting the set of format directives of a | |
| format string. Depends on FCHAR_T. | |
| PRINTF_PARSE Function that parses a format string. | |
| Depends on FCHAR_T. | |
| DCHAR_CPY memcpy like function for DCHAR_T[] arrays. | |
| DCHAR_SET memset like function for DCHAR_T[] arrays. | |
| DCHAR_STRLEN strlen like function for DCHAR_T[] arrays. | |
| DCHAR_MBSNLEN mbsnlen like function for DCHAR_T[] arrays. | |
| SNPRINTF The system's snprintf (or similar) function. | |
| This may be either snprintf or swprintf. | |
| TCHAR_T The element type of the argument and result string | |
| of the said SNPRINTF function. This may be either | |
| char or wchar_t. The code exploits that | |
| sizeof (TCHAR_T) | sizeof (DCHAR_T) and | |
| alignof (TCHAR_T) <= alignof (DCHAR_T). | |
| DCHAR_IS_TCHAR Set to 1 if DCHAR_T and TCHAR_T are the same type. | |
| DCHAR_CONV_FROM_ENCODING A function to convert from char[] to DCHAR[]. | |
| DCHAR_IS_UINT8_T Set to 1 if DCHAR_T is uint8_t. | |
| DCHAR_IS_UINT16_T Set to 1 if DCHAR_T is uint16_t. | |
| DCHAR_IS_UINT32_T Set to 1 if DCHAR_T is uint32_t. | |
| ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. | |
| ENABLE_WCHAR_FALLBACK Set to 1 to avoid EILSEQ during conversion of wide | |
| characters (wchar_t) and wide character strings | |
| (wchar_t[]) to multibyte sequences. The fallback is the | |
| hexadecimal escape syntax (\unnnn or \Unnnnnnnn) or, | |
| if wchar_t is not Unicode encoded, \wnnnn or \Wnnnnnnnn. | |
| */ | |
| /* Tell glibc's <stdio.h> to provide a prototype for snprintf(). | |
| This must come before <config.h> because <config.h> may include | |
| <features.h>, and once <features.h> has been included, it's too late. */ | |
| #ifndef _GNU_SOURCE | |
| # define _GNU_SOURCE 1 | |
| #endif | |
| #ifndef VASNPRINTF | |
| # include <config.h> | |
| #endif | |
| /* As of GCC 11.2.1, gcc -Wanalyzer-too-complex reports that main's | |
| use of CHECK macros expands to code that is too complicated for gcc | |
| -fanalyzer. Suppress the resulting bogus warnings. */ | |
| #if _GL_GNUC_PREREQ (10, 0) | |
| # pragma GCC diagnostic ignored "-Wanalyzer-null-argument" | |
| #endif | |
| #include <alloca.h> | |
| /* Specification. */ | |
| #ifndef VASNPRINTF | |
| # if WIDE_CHAR_VERSION | |
| # include "vasnwprintf.h" | |
| # else | |
| # include "vasnprintf.h" | |
| # endif | |
| #endif | |
| #include <locale.h> /* localeconv() */ | |
| #include <stdint.h> /* PTRDIFF_MAX */ | |
| #include <stdio.h> /* snprintf(), sprintf() */ | |
| #include <stdlib.h> /* abort(), malloc(), realloc(), free() */ | |
| #include <string.h> /* memcpy(), strlen() */ | |
| #include <wchar.h> /* mbstate_t, mbrtowc(), mbrlen(), wcrtomb(), mbszero() */ | |
| #include <errno.h> /* errno */ | |
| #include <limits.h> /* CHAR_BIT, INT_MAX, INT_WIDTH, LONG_WIDTH */ | |
| #include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP, LDBL_MANT_DIG */ | |
| #if HAVE_NL_LANGINFO || __GLIBC__ >= 2 || defined __CYGWIN__ | |
| # include <langinfo.h> | |
| #endif | |
| #ifndef VASNPRINTF | |
| # if WIDE_CHAR_VERSION | |
| # include "wprintf-parse.h" | |
| # else | |
| # include "printf-parse.h" | |
| # endif | |
| #endif | |
| /* Checked size_t computations. */ | |
| #include "xsize.h" | |
| #include "attribute.h" | |
| #if NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) | |
| # include <math.h> | |
| # include "float+.h" | |
| #endif | |
| #if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE | |
| # include <math.h> | |
| # include "isnand-nolibm.h" | |
| #endif | |
| #if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) | |
| # include <math.h> | |
| # include "isnanl-nolibm.h" | |
| # include "fpucw.h" | |
| #endif | |
| #if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE | |
| # include <math.h> | |
| # include "isnand-nolibm.h" | |
| # include "printf-frexp.h" | |
| #endif | |
| #if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) | |
| # include <math.h> | |
| # include "isnanl-nolibm.h" | |
| # include "printf-frexpl.h" | |
| # include "fpucw.h" | |
| #endif | |
| #if HAVE_SAME_LONG_DOUBLE_AS_DOUBLE | |
| /* Override macros from fpucw.h. */ | |
| # undef DECL_LONG_DOUBLE_ROUNDING | |
| # define DECL_LONG_DOUBLE_ROUNDING | |
| # undef BEGIN_LONG_DOUBLE_ROUNDING | |
| # define BEGIN_LONG_DOUBLE_ROUNDING() | |
| # undef END_LONG_DOUBLE_ROUNDING | |
| # define END_LONG_DOUBLE_ROUNDING() | |
| #endif | |
| /* Default parameters. */ | |
| #ifndef VASNPRINTF | |
| # if WIDE_CHAR_VERSION | |
| # define VASNPRINTF vasnwprintf | |
| # define FCHAR_T wchar_t | |
| # define DCHAR_T wchar_t | |
| # define DIRECTIVE wchar_t_directive | |
| # define DIRECTIVES wchar_t_directives | |
| # define PRINTF_PARSE wprintf_parse | |
| # define DCHAR_CPY wmemcpy | |
| # define DCHAR_SET wmemset | |
| # else | |
| # define VASNPRINTF vasnprintf | |
| # define FCHAR_T char | |
| # define DCHAR_T char | |
| # define TCHAR_T char | |
| # define DCHAR_IS_TCHAR 1 | |
| # define DIRECTIVE char_directive | |
| # define DIRECTIVES char_directives | |
| # define PRINTF_PARSE printf_parse | |
| # define DCHAR_CPY memcpy | |
| # define DCHAR_SET memset | |
| # endif | |
| #endif | |
| #if WIDE_CHAR_VERSION | |
| /* DCHAR_T is wchar_t. */ | |
| # if HAVE_DECL__SNWPRINTF || (HAVE_SWPRINTF && HAVE_WORKING_SWPRINTF) | |
| # define TCHAR_T wchar_t | |
| # define DCHAR_IS_TCHAR 1 | |
| # define USE_SNPRINTF 1 | |
| # if HAVE_DECL__SNWPRINTF | |
| /* On Windows, the function swprintf() has a different signature than | |
| on Unix; we use the function _snwprintf() or - on mingw - snwprintf() | |
| instead. The mingw function snwprintf() has fewer bugs than the | |
| MSVCRT function _snwprintf(), so prefer that. */ | |
| # if defined __MINGW32__ | |
| # define SNPRINTF snwprintf | |
| # else | |
| # define SNPRINTF _snwprintf | |
| # define USE_MSVC__SNPRINTF 1 | |
| # endif | |
| # else | |
| /* Unix. */ | |
| # define SNPRINTF swprintf | |
| # endif | |
| # else | |
| /* Old platforms such as NetBSD 3.0, OpenBSD 3.8, HP-UX 11.00. */ | |
| # define TCHAR_T char | |
| # endif | |
| #endif | |
| #ifndef DCHAR_STRLEN | |
| # if WIDE_CHAR_VERSION | |
| # define DCHAR_STRLEN local_wcslen | |
| # else | |
| # define DCHAR_STRLEN strlen | |
| # endif | |
| #endif | |
| #ifndef DCHAR_MBSNLEN | |
| # if WIDE_CHAR_VERSION | |
| # define DCHAR_MBSNLEN wcsnlen | |
| # else | |
| # define DCHAR_MBSNLEN mbsnlen | |
| # endif | |
| #endif | |
| #if !WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR | |
| /* TCHAR_T is char. */ | |
| /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. | |
| But don't use it on BeOS, since BeOS snprintf produces no output if the | |
| size argument is >= 0x3000000. | |
| Also don't use it on Linux libc5, since there snprintf with size = 1 | |
| writes any output without bounds, like sprintf. */ | |
| # if (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) && !defined __BEOS__ && !(__GNU_LIBRARY__ == 1) | |
| # define USE_SNPRINTF 1 | |
| # else | |
| # define USE_SNPRINTF 0 | |
| # endif | |
| # if HAVE_DECL__SNPRINTF | |
| /* Windows. The mingw function snprintf() has fewer bugs than the MSVCRT | |
| function _snprintf(), so prefer that. */ | |
| # if defined __MINGW32__ | |
| # define SNPRINTF snprintf | |
| /* Here we need to call the native snprintf, not rpl_snprintf. */ | |
| # undef snprintf | |
| # else | |
| /* MSVC versions < 14 did not have snprintf, only _snprintf. */ | |
| # define SNPRINTF _snprintf | |
| # define USE_MSVC__SNPRINTF 1 | |
| # endif | |
| # else | |
| /* Unix. */ | |
| # define SNPRINTF snprintf | |
| /* Here we need to call the native snprintf, not rpl_snprintf. */ | |
| # undef snprintf | |
| # endif | |
| #endif | |
| /* Here we need to call the native sprintf, not rpl_sprintf. */ | |
| #undef sprintf | |
| /* macOS 12's "warning: 'sprintf' is deprecated" is pointless, | |
| as sprintf is used safely here. */ | |
| #if defined __APPLE__ && defined __MACH__ && _GL_GNUC_PREREQ (4, 2) | |
| # pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |
| #endif | |
| /* GCC >= 4.0 with -Wall emits unjustified "... may be used uninitialized" | |
| warnings in this file. Use -Dlint to suppress them. */ | |
| #if defined GCC_LINT || defined lint | |
| # define IF_LINT(Code) Code | |
| #else | |
| # define IF_LINT(Code) /* empty */ | |
| #endif | |
| /* Here we need only the most basic fields of 'struct lconv', and can | |
| therefore use the system's localeconv() function, without needing a | |
| dependency on module 'localeconv'. */ | |
| #undef localeconv | |
| /* Avoid some warnings from "gcc -Wshadow". | |
| This file doesn't use the exp() and remainder() functions. */ | |
| #undef exp | |
| #define exp expo | |
| #undef remainder | |
| #define remainder rem | |
| #if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (PTRDIFF_MAX > INT_MAX)) && !WIDE_CHAR_VERSION | |
| # if (HAVE_STRNLEN && !defined _AIX) | |
| # define local_strnlen strnlen | |
| # else | |
| # ifndef local_strnlen_defined | |
| # define local_strnlen_defined 1 | |
| static size_t | |
| local_strnlen (const char *string, size_t maxlen) | |
| { | |
| const char *end = memchr (string, '\0', maxlen); | |
| return end ? (size_t) (end - string) : maxlen; | |
| } | |
| # endif | |
| # endif | |
| #endif | |
| #if ((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !DCHAR_IS_TCHAR || NEED_WPRINTF_DIRECTIVE_LC) && WIDE_CHAR_VERSION) || ((!USE_SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS) && !WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) | |
| # if HAVE_WCSLEN | |
| # define local_wcslen wcslen | |
| # else | |
| /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid | |
| a dependency towards this library, here is a local substitute. | |
| Define this substitute only once, even if this file is included | |
| twice in the same compilation unit. */ | |
| # ifndef local_wcslen_defined | |
| # define local_wcslen_defined 1 | |
| static size_t | |
| local_wcslen (const wchar_t *s) | |
| { | |
| const wchar_t *ptr; | |
| for (ptr = s; *ptr != (wchar_t) 0; ptr++) | |
| ; | |
| return ptr - s; | |
| } | |
| # endif | |
| # endif | |
| #endif | |
| #if (!USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && WIDE_CHAR_VERSION | |
| # if HAVE_WCSNLEN && HAVE_DECL_WCSNLEN | |
| # define local_wcsnlen wcsnlen | |
| # else | |
| # ifndef local_wcsnlen_defined | |
| # define local_wcsnlen_defined 1 | |
| static size_t | |
| local_wcsnlen (const wchar_t *s, size_t maxlen) | |
| { | |
| const wchar_t *ptr; | |
| for (ptr = s; maxlen > 0 && *ptr != (wchar_t) 0; ptr++, maxlen--) | |
| ; | |
| return ptr - s; | |
| } | |
| # endif | |
| # endif | |
| #endif | |
| #if ((!USE_SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T)) && !WIDE_CHAR_VERSION | |
| # if ENABLE_WCHAR_FALLBACK | |
| static size_t | |
| wctomb_fallback (char *s, wchar_t wc) | |
| { | |
| static char const hex[16] = "0123456789ABCDEF"; | |
| s[0] = '\\'; | |
| if (sizeof (wchar_t) > 2 && wc > 0xffff) | |
| { | |
| # if __STDC_ISO_10646__ || (__GLIBC__ >= 2) || (defined _WIN32 || defined __CYGWIN__) | |
| s[1] = 'U'; | |
| # else | |
| s[1] = 'W'; | |
| # endif | |
| s[2] = hex[(wc & 0xf0000000U) >> 28]; | |
| s[3] = hex[(wc & 0xf000000U) >> 24]; | |
| s[4] = hex[(wc & 0xf00000U) >> 20]; | |
| s[5] = hex[(wc & 0xf0000U) >> 16]; | |
| s[6] = hex[(wc & 0xf000U) >> 12]; | |
| s[7] = hex[(wc & 0xf00U) >> 8]; | |
| s[8] = hex[(wc & 0xf0U) >> 4]; | |
| s[9] = hex[wc & 0xfU]; | |
| return 10; | |
| } | |
| else | |
| { | |
| # if __STDC_ISO_10646__ || (__GLIBC__ >= 2) || (defined _WIN32 || defined __CYGWIN__) | |
| s[1] = 'u'; | |
| # else | |
| s[1] = 'w'; | |
| # endif | |
| s[2] = hex[(wc & 0xf000U) >> 12]; | |
| s[3] = hex[(wc & 0xf00U) >> 8]; | |
| s[4] = hex[(wc & 0xf0U) >> 4]; | |
| s[5] = hex[wc & 0xfU]; | |
| return 6; | |
| } | |
| } | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| static size_t | |
| local_wcrtomb (char *s, wchar_t wc, mbstate_t *ps) | |
| { | |
| size_t count = wcrtomb (s, wc, ps); | |
| if (count == (size_t)(-1)) | |
| count = wctomb_fallback (s, wc); | |
| return count; | |
| } | |
| # else | |
| static int | |
| local_wctomb (char *s, wchar_t wc) | |
| { | |
| int count = wctomb (s, wc); | |
| if (count < 0) | |
| count = wctomb_fallback (s, wc); | |
| return count; | |
| } | |
| # define local_wcrtomb(S, WC, PS) local_wctomb ((S), (WC)) | |
| # endif | |
| # else | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| # define local_wcrtomb(S, WC, PS) wcrtomb ((S), (WC), (PS)) | |
| # else | |
| # define local_wcrtomb(S, WC, PS) wctomb ((S), (WC)) | |
| # endif | |
| # endif | |
| #endif | |
| #if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) || (NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT) | |
| /* Determine the decimal-point character according to the current locale. */ | |
| # ifndef decimal_point_char_defined | |
| # define decimal_point_char_defined 1 | |
| static char | |
| decimal_point_char (void) | |
| { | |
| const char *point; | |
| /* Determine it in a multithread-safe way. We know nl_langinfo is | |
| multithread-safe on glibc systems and Mac OS X systems, but is not required | |
| to be multithread-safe by POSIX. sprintf(), however, is multithread-safe. | |
| localeconv() is rarely multithread-safe. */ | |
| # if HAVE_NL_LANGINFO && (__GLIBC__ || defined __UCLIBC__ || (defined __APPLE__ && defined __MACH__)) | |
| point = nl_langinfo (RADIXCHAR); | |
| # elif 1 | |
| char pointbuf[5]; | |
| sprintf (pointbuf, "%#.0f", 1.0); | |
| point = &pointbuf[1]; | |
| # else | |
| point = localeconv () -> decimal_point; | |
| # endif | |
| /* The decimal point is always a single byte: either '.' or ','. */ | |
| return (point[0] != '\0' ? point[0] : '.'); | |
| } | |
| # endif | |
| #endif | |
| #if (!WIDE_CHAR_VERSION && (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE)) || ((!WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR) && (NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT)) | |
| /* Determine the thousands-separator character according to the current | |
| locale. | |
| It is a single multibyte character. | |
| In glibc: 35x ".", 90x ",", 23x U+202F, 1x U+2019, 1x U+066C, on other | |
| systems also U+00A0. */ | |
| # ifndef thousands_separator_char_defined | |
| # define thousands_separator_char_defined 1 | |
| static const char * | |
| thousands_separator_char (char stackbuf[10]) | |
| { | |
| /* Determine it in a multithread-safe way. | |
| We know nl_langinfo is multithread-safe on glibc systems, on Mac OS X | |
| systems, and on NetBSD, but is not required to be multithread-safe by | |
| POSIX. | |
| localeconv() is not guaranteed to be multithread-safe by POSIX either; | |
| however, on native Windows it is (cf. test-localeconv-mt). | |
| sprintf(), however, is multithread-safe. */ | |
| # if HAVE_NL_LANGINFO && (__GLIBC__ || defined __UCLIBC__ || (defined __APPLE__ && defined __MACH__) || defined __NetBSD__) | |
| return nl_langinfo (THOUSEP); | |
| # elif defined _WIN32 && !defined __CYGWIN__ | |
| return localeconv () -> thousands_sep; | |
| # else | |
| sprintf (stackbuf, "%'.0f", 1000.0); | |
| /* Now stackbuf = "1<thousep>000". */ | |
| stackbuf[strlen (stackbuf) - 3] = '\0'; | |
| # if defined __sun | |
| /* Solaris specific hack: Replace wrong result (0xC2 means U+00A0). */ | |
| if (streq (&stackbuf[1], "\302")) | |
| strcpy (&stackbuf[1], MB_CUR_MAX > 1 ? "\302\240" : "\240"); | |
| # endif | |
| return &stackbuf[1]; | |
| # endif | |
| } | |
| # endif | |
| #endif | |
| #if !WIDE_CHAR_VERSION && defined DCHAR_CONV_FROM_ENCODING && (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) | |
| /* Determine the thousands-separator character, as a DCHAR_T[] array, | |
| according to the current locale. | |
| It is a single Unicode character. */ | |
| # ifndef thousands_separator_DCHAR_defined | |
| # define thousands_separator_DCHAR_defined 1 | |
| static const DCHAR_T * | |
| thousands_separator_DCHAR (DCHAR_T stackbuf[10]) | |
| { | |
| /* Determine it in a multithread-safe way. */ | |
| char tmpbuf[10]; | |
| const char *tmp = thousands_separator_char (tmpbuf); | |
| if (*tmp != '\0') | |
| { | |
| /* Convert it from char[] to DCHAR_T[]. */ | |
| size_t converted_len = 10; | |
| DCHAR_T *converted = | |
| DCHAR_CONV_FROM_ENCODING (locale_charset (), | |
| iconveh_question_mark, | |
| tmp, strlen (tmp) + 1, | |
| NULL, | |
| stackbuf, &converted_len); | |
| if (converted != NULL) | |
| { | |
| if (converted != stackbuf) | |
| /* It should not be so long. */ | |
| abort (); | |
| return stackbuf; | |
| } | |
| } | |
| stackbuf[0] = 0; | |
| return stackbuf; | |
| } | |
| # endif | |
| #endif | |
| /* Maximum number of 'char' in the char[] or DCHAR_T[] representation of the | |
| thousands separator. */ | |
| #define THOUSEP_CHAR_MAXLEN 3 | |
| #if WIDE_CHAR_VERSION && ((NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) || ((NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT) && DCHAR_IS_TCHAR)) | |
| /* Determine the thousands-separator character, as a wide character, according | |
| to the current locale. | |
| It is a single wide character. */ | |
| # ifndef thousands_separator_wchar_defined | |
| # define thousands_separator_wchar_defined 1 | |
| static const wchar_t * | |
| thousands_separator_wchar (wchar_t stackbuf[10]) | |
| { | |
| # if __GLIBC__ >= 2 || defined __CYGWIN__ | |
| /* On glibc, in the unibyte locale fr_FR, the *wprintf routines use U+202F | |
| as separator, which cannot be represented in the locale encoding. */ | |
| stackbuf[0] = | |
| (wchar_t) (unsigned long) nl_langinfo (_NL_NUMERIC_THOUSANDS_SEP_WC); | |
| stackbuf[1] = L'\0'; | |
| return stackbuf; | |
| # elif defined _WIN32 && !defined __CYGWIN__ | |
| const char *tmp = localeconv () -> thousands_sep; | |
| if (*tmp != '\0') | |
| { | |
| mbstate_t state; | |
| mbszero (&state); | |
| if ((int) mbrtowc (&stackbuf[0], tmp, strlen (tmp), &state) > 0) | |
| stackbuf[1] = L'\0'; | |
| else | |
| stackbuf[0] = L'\0'; | |
| } | |
| else | |
| stackbuf[0] = L'\0'; | |
| return stackbuf; | |
| # elif defined __sun | |
| /* Use sprintf, because swprintf retrieves a wrong value for the | |
| thousands-separator wide character (e.g. (wchar_t) 0xffffffa0). */ | |
| char tmp[10]; | |
| sprintf (tmp, "%'.0f", 1000.0); | |
| /* Now tmp = L"1<thousep>000". */ | |
| tmp[strlen (tmp) - 3] = '\0'; | |
| /* Solaris specific hack: Replace wrong result (0xC2 means U+00A0). */ | |
| if (streq (&tmp[1], "\302")) | |
| strcpy (&tmp[1], MB_CUR_MAX > 1 ? "\302\240" : "\240"); | |
| if (tmp[1] != '\0') | |
| { | |
| mbstate_t state; | |
| mbszero (&state); | |
| if ((int) mbrtowc (&stackbuf[0], &tmp[1], strlen (&tmp[1]), &state) > 0) | |
| stackbuf[1] = L'\0'; | |
| else | |
| stackbuf[0] = L'\0'; | |
| } | |
| else | |
| stackbuf[0] = L'\0'; | |
| return stackbuf; | |
| # else | |
| swprintf (stackbuf, 10, L"%'.0f", 1000.0); | |
| /* Now stackbuf = L"1<thousep>000". */ | |
| stackbuf[local_wcslen (stackbuf) - 3] = '\0'; | |
| return &stackbuf[1]; | |
| # endif | |
| } | |
| # endif | |
| #endif | |
| /* Maximum number of 'wchar_t' in the wchar_t[] representation of the thousands | |
| separator. */ | |
| #define THOUSEP_WCHAR_MAXLEN 1 | |
| #if (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) || (NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT) | |
| # ifndef grouping_rule_defined | |
| # define grouping_rule_defined 1 | |
| /* Determine the grouping rule. | |
| * As specified in POSIX | |
| * <https://pubs.opengroup.org/onlinepubs/9799919799/functions/localeconv.html> | |
| * <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html#tag_07_03_04> | |
| * it is a string whose elements are 'signed char' values, where | |
| * "Each integer specifies the number of digits in each group, with the initial | |
| * integer defining the size of the group immediately preceding the decimal | |
| * delimiter, and the following integers defining the preceding groups. If | |
| * the last integer is not -1, then the size of the previous group (if any) | |
| * shall be repeatedly used for the remainder of the digits. If the last | |
| * integer is -1, then no further grouping shall be performed." | |
| * Platforms that have locales with grouping: | |
| * glibc, FreeBSD, NetBSD, AIX, Solaris, Cygwin, Haiku. | |
| * Platforms that don't: | |
| * musl libc, macOS, OpenBSD, Android, mingw, MSVC. | |
| * Typical grouping rules on glibc: | |
| * 136x 3 (fr_FR etc.) | |
| * 4x 4 (cmn_TW etc.) | |
| * 9x 3;2 (ta_IN etc.) | |
| * 1x 2;2;2;3 (umn_US) | |
| * 21x -1 (C etc.) | |
| */ | |
| static const signed char * | |
| grouping_rule (void) | |
| { | |
| /* We know nl_langinfo is multithread-safe on glibc systems and on Cygwin, | |
| but is not required to be multithread-safe by POSIX. | |
| localeconv() is not guaranteed to be multithread-safe by POSIX either; | |
| however, on all known systems it is (cf. test-localeconv-mt). */ | |
| # if __GLIBC__ >= 2 | |
| return (const signed char *) nl_langinfo (GROUPING); | |
| # elif defined __CYGWIN__ | |
| return (const signed char *) nl_langinfo (_NL_NUMERIC_GROUPING); | |
| # else | |
| return (const signed char *) localeconv () -> grouping; | |
| # endif | |
| } | |
| /* Determines the number of thousands-separators to be inserted in a digit | |
| sequence with ndigits digits (before the decimal point). */ | |
| static size_t | |
| num_thousands_separators (const signed char *grouping, size_t ndigits) | |
| { | |
| const signed char *g = grouping; | |
| int h = *g; | |
| if (h <= 0 || ndigits == 0) | |
| return 0; | |
| size_t insert = 0; | |
| for (;;) | |
| { | |
| /* Invariant: here h == *g, h > 0, ndigits > 0. */ | |
| if (g[1] == 0) | |
| /* h repeats endlessly. */ | |
| return insert + (ndigits - 1) / h; | |
| /* h does not repeat. */ | |
| if (ndigits <= h) | |
| return insert; | |
| ndigits -= h; | |
| insert++; | |
| g++; | |
| h = *g; | |
| if (h < 0) | |
| /* No further grouping. */ | |
| return insert; | |
| } | |
| } | |
| # endif | |
| #endif | |
| #if NEED_PRINTF_INFINITE_DOUBLE && !NEED_PRINTF_DOUBLE | |
| /* Equivalent to !isfinite(x) || x == 0, but does not require libm. */ | |
| static int | |
| is_infinite_or_zero (double x) | |
| { | |
| return isnand (x) || x + x == x; | |
| } | |
| #endif | |
| #if NEED_PRINTF_INFINITE_LONG_DOUBLE && !NEED_PRINTF_LONG_DOUBLE | |
| /* Equivalent to !isfinite(x) || x == 0, but does not require libm. */ | |
| static int | |
| is_infinite_or_zerol (long double x) | |
| { | |
| return isnanl (x) || x + x == x; | |
| } | |
| #endif | |
| #if NEED_PRINTF_LONG_DOUBLE | |
| /* Like frexpl, except that it supports even "unsupported" numbers. */ | |
| # if (LDBL_MANT_DIG == 64 && (defined __ia64 || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_))) && (defined __APPLE__ && defined __MACH__) | |
| /* Don't assume that frexpl can handle pseudo-denormals; it does not on | |
| macOS 12/x86_64. Therefore test for a pseudo-denormal explicitly. */ | |
| static | |
| long double safe_frexpl (long double x, int *exp) | |
| { | |
| union | |
| { | |
| long double value; | |
| struct { unsigned int mant_word[2]; unsigned short sign_exp_word; } r; | |
| } | |
| u; | |
| u.value = x; | |
| if (u.r.sign_exp_word == 0 && (u.r.mant_word[1] & 0x80000000u) != 0) | |
| { | |
| /* Pseudo-Denormal. */ | |
| *exp = LDBL_MIN_EXP; | |
| u.r.sign_exp_word = 1 - LDBL_MIN_EXP; | |
| return u.value; | |
| } | |
| else | |
| return frexpl (x, exp); | |
| } | |
| # else | |
| # define safe_frexpl frexpl | |
| # endif | |
| #endif | |
| #if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE | |
| /* An indicator for a failed memory allocation. */ | |
| # define NOMEM_PTR ((void *) (-1)) | |
| /* Converting 'long double' to decimal without rare rounding bugs requires | |
| real bignums. We use the naming conventions of GNU gmp, but vastly simpler | |
| (and slower) algorithms. */ | |
| typedef unsigned int mp_limb_t; | |
| # define GMP_LIMB_BITS 32 | |
| static_assert (sizeof (mp_limb_t) * CHAR_BIT == GMP_LIMB_BITS); | |
| typedef unsigned long long mp_twolimb_t; | |
| # define GMP_TWOLIMB_BITS 64 | |
| static_assert (sizeof (mp_twolimb_t) * CHAR_BIT == GMP_TWOLIMB_BITS); | |
| /* Representation of a bignum >= 0. */ | |
| typedef struct | |
| { | |
| size_t nlimbs; | |
| mp_limb_t *limbs; /* Bits in little-endian order, allocated with malloc(). */ | |
| } mpn_t; | |
| /* Compute the product of two bignums >= 0. | |
| Return the allocated memory (possibly NULL) in case of success, NOMEM_PTR | |
| in case of memory allocation failure. */ | |
| static void * | |
| multiply (mpn_t src1, mpn_t src2, mpn_t *dest) | |
| { | |
| const mp_limb_t *p1; | |
| const mp_limb_t *p2; | |
| size_t len1; | |
| size_t len2; | |
| if (src1.nlimbs <= src2.nlimbs) | |
| { | |
| len1 = src1.nlimbs; | |
| p1 = src1.limbs; | |
| len2 = src2.nlimbs; | |
| p2 = src2.limbs; | |
| } | |
| else | |
| { | |
| len1 = src2.nlimbs; | |
| p1 = src2.limbs; | |
| len2 = src1.nlimbs; | |
| p2 = src1.limbs; | |
| } | |
| /* Now 0 <= len1 <= len2. */ | |
| if (len1 == 0) | |
| { | |
| /* src1 or src2 is zero. */ | |
| dest->nlimbs = 0; | |
| dest->limbs = NULL; | |
| } | |
| else | |
| { | |
| /* Here 1 <= len1 <= len2. */ | |
| size_t dlen; | |
| mp_limb_t *dp; | |
| dlen = len1 + len2; | |
| dp = (mp_limb_t *) malloc (dlen * sizeof (mp_limb_t)); | |
| if (dp == NULL) | |
| return NOMEM_PTR; | |
| for (size_t k = len2; k > 0; ) | |
| dp[--k] = 0; | |
| for (size_t i = 0; i < len1; i++) | |
| { | |
| mp_limb_t digit1 = p1[i]; | |
| mp_twolimb_t carry = 0; | |
| for (size_t j = 0; j < len2; j++) | |
| { | |
| mp_limb_t digit2 = p2[j]; | |
| carry += (mp_twolimb_t) digit1 * (mp_twolimb_t) digit2; | |
| carry += dp[i + j]; | |
| dp[i + j] = (mp_limb_t) carry; | |
| carry = carry >> GMP_LIMB_BITS; | |
| } | |
| dp[i + len2] = (mp_limb_t) carry; | |
| } | |
| /* Normalise. */ | |
| while (dlen > 0 && dp[dlen - 1] == 0) | |
| dlen--; | |
| dest->nlimbs = dlen; | |
| dest->limbs = dp; | |
| } | |
| return dest->limbs; | |
| } | |
| /* Compute the quotient of a bignum a >= 0 and a bignum b > 0. | |
| a is written as a = q * b + r with 0 <= r < b. q is the quotient, r | |
| the remainder. | |
| Finally, round-to-even is performed: If r > b/2 or if r = b/2 and q is odd, | |
| q is incremented. | |
| Return the allocated memory (possibly NULL) in case of success, NOMEM_PTR | |
| in case of memory allocation failure. */ | |
| static void * | |
| divide (mpn_t a, mpn_t b, mpn_t *q) | |
| { | |
| /* Algorithm: | |
| First normalise a and b: a=[a[m-1],...,a[0]], b=[b[n-1],...,b[0]] | |
| with m>=0 and n>0 (in base beta = 2^GMP_LIMB_BITS). | |
| If m<n, then q:=0 and r:=a. | |
| If m>=n=1, perform a single-precision division: | |
| r:=0, j:=m, | |
| while j>0 do | |
| {Here (q[m-1]*beta^(m-1)+...+q[j]*beta^j) * b[0] + r*beta^j = | |
| = a[m-1]*beta^(m-1)+...+a[j]*beta^j und 0<=r<b[0]<beta} | |
| j:=j-1, r:=r*beta+a[j], q[j]:=floor(r/b[0]), r:=r-b[0]*q[j]. | |
| Normalise [q[m-1],...,q[0]], yields q. | |
| If m>=n>1, perform a multiple-precision division: | |
| We have a/b < beta^(m-n+1). | |
| s:=intDsize-1-(highest bit in b[n-1]), 0<=s<intDsize. | |
| Shift a and b left by s bits, copying them. r:=a. | |
| r=[r[m],...,r[0]], b=[b[n-1],...,b[0]] with b[n-1]>=beta/2. | |
| For j=m-n,...,0: {Here 0 <= r < b*beta^(j+1).} | |
| Compute q* : | |
| q* := floor((r[j+n]*beta+r[j+n-1])/b[n-1]). | |
| In case of overflow (q* >= beta) set q* := beta-1. | |
| Compute c2 := ((r[j+n]*beta+r[j+n-1]) - q* * b[n-1])*beta + r[j+n-2] | |
| and c3 := b[n-2] * q*. | |
| {We have 0 <= c2 < 2*beta^2, even 0 <= c2 < beta^2 if no overflow | |
| occurred. Furthermore 0 <= c3 < beta^2. | |
| If there was overflow and | |
| r[j+n]*beta+r[j+n-1] - q* * b[n-1] >= beta, i.e. c2 >= beta^2, | |
| the next test can be skipped.} | |
| While c3 > c2, {Here 0 <= c2 < c3 < beta^2} | |
| Put q* := q* - 1, c2 := c2 + b[n-1]*beta, c3 := c3 - b[n-2]. | |
| If q* > 0: | |
| Put r := r - b * q* * beta^j. In detail: | |
| [r[n+j],...,r[j]] := [r[n+j],...,r[j]] - q* * [b[n-1],...,b[0]]. | |
| hence: u:=0, for i:=0 to n-1 do | |
| u := u + q* * b[i], | |
| r[j+i]:=r[j+i]-(u mod beta) (+ beta, if carry), | |
| u:=u div beta (+ 1, if carry in subtraction) | |
| r[n+j]:=r[n+j]-u. | |
| {Since always u = (q* * [b[i-1],...,b[0]] div beta^i) + 1 | |
| < q* + 1 <= beta, | |
| the carry u does not overflow.} | |
| If a negative carry occurs, put q* := q* - 1 | |
| and [r[n+j],...,r[j]] := [r[n+j],...,r[j]] + [0,b[n-1],...,b[0]]. | |
| Set q[j] := q*. | |
| Normalise [q[m-n],..,q[0]]; this yields the quotient q. | |
| Shift [r[n-1],...,r[0]] right by s bits and normalise; this yields the | |
| rest r. | |
| The room for q[j] can be allocated at the memory location of r[n+j]. | |
| Finally, round-to-even: | |
| Shift r left by 1 bit. | |
| If r > b or if r = b and q[0] is odd, q := q+1. | |
| */ | |
| const mp_limb_t *a_ptr = a.limbs; | |
| size_t a_len = a.nlimbs; | |
| const mp_limb_t *b_ptr = b.limbs; | |
| size_t b_len = b.nlimbs; | |
| mp_limb_t *tmp_roomptr = NULL; | |
| mp_limb_t *q_ptr; | |
| size_t q_len; | |
| mp_limb_t *r_ptr; | |
| size_t r_len; | |
| /* Allocate room for a_len+2 digits. | |
| (Need a_len+1 digits for the real division and 1 more digit for the | |
| final rounding of q.) */ | |
| mp_limb_t *roomptr = (mp_limb_t *) malloc ((a_len + 2) * sizeof (mp_limb_t)); | |
| if (roomptr == NULL) | |
| return NOMEM_PTR; | |
| /* Normalise a. */ | |
| while (a_len > 0 && a_ptr[a_len - 1] == 0) | |
| a_len--; | |
| /* Normalise b. */ | |
| for (;;) | |
| { | |
| if (b_len == 0) | |
| /* Division by zero. */ | |
| abort (); | |
| if (b_ptr[b_len - 1] == 0) | |
| b_len--; | |
| else | |
| break; | |
| } | |
| /* Here m = a_len >= 0 and n = b_len > 0. */ | |
| if (a_len < b_len) | |
| { | |
| /* m<n: trivial case. q=0, r := copy of a. */ | |
| r_ptr = roomptr; | |
| r_len = a_len; | |
| memcpy (r_ptr, a_ptr, a_len * sizeof (mp_limb_t)); | |
| q_ptr = roomptr + a_len; | |
| q_len = 0; | |
| } | |
| else if (b_len == 1) | |
| { | |
| /* n=1: single precision division. | |
| beta^(m-1) <= a < beta^m ==> beta^(m-2) <= a/b < beta^m */ | |
| r_ptr = roomptr; | |
| q_ptr = roomptr + 1; | |
| { | |
| mp_limb_t den = b_ptr[0]; | |
| mp_limb_t remainder = 0; | |
| const mp_limb_t *sourceptr = a_ptr + a_len; | |
| mp_limb_t *destptr = q_ptr + a_len; | |
| for (size_t count = a_len; count > 0; count--) | |
| { | |
| mp_twolimb_t num = | |
| ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--sourceptr; | |
| *--destptr = num / den; | |
| remainder = num % den; | |
| } | |
| /* Normalise and store r. */ | |
| if (remainder > 0) | |
| { | |
| r_ptr[0] = remainder; | |
| r_len = 1; | |
| } | |
| else | |
| r_len = 0; | |
| /* Normalise q. */ | |
| q_len = a_len; | |
| if (q_ptr[q_len - 1] == 0) | |
| q_len--; | |
| } | |
| } | |
| else | |
| { | |
| /* n>1: multiple precision division. | |
| beta^(m-1) <= a < beta^m, beta^(n-1) <= b < beta^n ==> | |
| beta^(m-n-1) <= a/b < beta^(m-n+1). */ | |
| /* Determine s. */ | |
| size_t s; | |
| { | |
| mp_limb_t msd = b_ptr[b_len - 1]; /* = b[n-1], > 0 */ | |
| /* Determine s = GMP_LIMB_BITS - integer_length (msd). | |
| Code copied from gnulib's integer_length.c. */ | |
| # if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) \ | |
| || (__clang_major__ >= 4) | |
| s = __builtin_clz (msd); | |
| # else | |
| # if defined DBL_EXPBIT0_WORD && defined DBL_EXPBIT0_BIT | |
| if (GMP_LIMB_BITS <= DBL_MANT_BIT) | |
| { | |
| /* Use 'double' operations. | |
| Assumes an IEEE 754 'double' implementation. */ | |
| # define DBL_EXP_MASK ((DBL_MAX_EXP - DBL_MIN_EXP) | 7) | |
| # define DBL_EXP_BIAS (DBL_EXP_MASK / 2 - 1) | |
| # define NWORDS \ | |
| ((sizeof (double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) | |
| union { double value; unsigned int word[NWORDS]; } m; | |
| /* Use a single integer to floating-point conversion. */ | |
| m.value = msd; | |
| s = GMP_LIMB_BITS | |
| - (((m.word[DBL_EXPBIT0_WORD] >> DBL_EXPBIT0_BIT) & DBL_EXP_MASK) | |
| - DBL_EXP_BIAS); | |
| } | |
| else | |
| # undef NWORDS | |
| # endif | |
| { | |
| s = 31; | |
| if (msd >= 0x10000) | |
| { | |
| msd = msd >> 16; | |
| s -= 16; | |
| } | |
| if (msd >= 0x100) | |
| { | |
| msd = msd >> 8; | |
| s -= 8; | |
| } | |
| if (msd >= 0x10) | |
| { | |
| msd = msd >> 4; | |
| s -= 4; | |
| } | |
| if (msd >= 0x4) | |
| { | |
| msd = msd >> 2; | |
| s -= 2; | |
| } | |
| if (msd >= 0x2) | |
| { | |
| msd = msd >> 1; | |
| s -= 1; | |
| } | |
| } | |
| # endif | |
| } | |
| /* 0 <= s < GMP_LIMB_BITS. | |
| Copy b, shifting it left by s bits. */ | |
| if (s > 0) | |
| { | |
| tmp_roomptr = (mp_limb_t *) malloc (b_len * sizeof (mp_limb_t)); | |
| if (tmp_roomptr == NULL) | |
| { | |
| free (roomptr); | |
| return NOMEM_PTR; | |
| } | |
| { | |
| const mp_limb_t *sourceptr = b_ptr; | |
| mp_limb_t *destptr = tmp_roomptr; | |
| mp_twolimb_t accu = 0; | |
| for (size_t count = b_len; count > 0; count--) | |
| { | |
| accu += (mp_twolimb_t) *sourceptr++ << s; | |
| *destptr++ = (mp_limb_t) accu; | |
| accu = accu >> GMP_LIMB_BITS; | |
| } | |
| /* accu must be zero, since that was how s was determined. */ | |
| if (accu != 0) | |
| abort (); | |
| } | |
| b_ptr = tmp_roomptr; | |
| } | |
| /* Copy a, shifting it left by s bits, yields r. | |
| Memory layout: | |
| At the beginning: r = roomptr[0..a_len], | |
| at the end: r = roomptr[0..b_len-1], q = roomptr[b_len..a_len] */ | |
| r_ptr = roomptr; | |
| if (s == 0) | |
| { | |
| memcpy (r_ptr, a_ptr, a_len * sizeof (mp_limb_t)); | |
| r_ptr[a_len] = 0; | |
| } | |
| else | |
| { | |
| const mp_limb_t *sourceptr = a_ptr; | |
| mp_limb_t *destptr = r_ptr; | |
| mp_twolimb_t accu = 0; | |
| for (size_t count = a_len; count > 0; count--) | |
| { | |
| accu += (mp_twolimb_t) *sourceptr++ << s; | |
| *destptr++ = (mp_limb_t) accu; | |
| accu = accu >> GMP_LIMB_BITS; | |
| } | |
| *destptr++ = (mp_limb_t) accu; | |
| } | |
| q_ptr = roomptr + b_len; | |
| q_len = a_len - b_len + 1; /* q will have m-n+1 limbs */ | |
| { | |
| size_t j = a_len - b_len; /* m-n */ | |
| mp_limb_t b_msd = b_ptr[b_len - 1]; /* b[n-1] */ | |
| mp_limb_t b_2msd = b_ptr[b_len - 2]; /* b[n-2] */ | |
| mp_twolimb_t b_msdd = /* b[n-1]*beta+b[n-2] */ | |
| ((mp_twolimb_t) b_msd << GMP_LIMB_BITS) | b_2msd; | |
| /* Division loop, traversed m-n+1 times. | |
| j counts down, b is unchanged, beta/2 <= b[n-1] < beta. */ | |
| for (;;) | |
| { | |
| mp_limb_t q_star; | |
| mp_limb_t c1; | |
| if (r_ptr[j + b_len] < b_msd) /* r[j+n] < b[n-1] ? */ | |
| { | |
| /* Divide r[j+n]*beta+r[j+n-1] by b[n-1], no overflow. */ | |
| mp_twolimb_t num = | |
| ((mp_twolimb_t) r_ptr[j + b_len] << GMP_LIMB_BITS) | |
| | r_ptr[j + b_len - 1]; | |
| q_star = num / b_msd; | |
| c1 = num % b_msd; | |
| } | |
| else | |
| { | |
| /* Overflow, hence r[j+n]*beta+r[j+n-1] >= beta*b[n-1]. */ | |
| q_star = (mp_limb_t)~(mp_limb_t)0; /* q* = beta-1 */ | |
| /* Test whether r[j+n]*beta+r[j+n-1] - (beta-1)*b[n-1] >= beta | |
| <==> r[j+n]*beta+r[j+n-1] + b[n-1] >= beta*b[n-1]+beta | |
| <==> b[n-1] < floor((r[j+n]*beta+r[j+n-1]+b[n-1])/beta) | |
| {<= beta !}. | |
| If yes, jump directly to the subtraction loop. | |
| (Otherwise, r[j+n]*beta+r[j+n-1] - (beta-1)*b[n-1] < beta | |
| <==> floor((r[j+n]*beta+r[j+n-1]+b[n-1])/beta) = b[n-1] ) */ | |
| if (r_ptr[j + b_len] > b_msd | |
| || (c1 = r_ptr[j + b_len - 1] + b_msd) < b_msd) | |
| /* r[j+n] >= b[n-1]+1 or | |
| r[j+n] = b[n-1] and the addition r[j+n-1]+b[n-1] gives a | |
| carry. */ | |
| goto subtract; | |
| } | |
| /* q_star = q*, | |
| c1 = (r[j+n]*beta+r[j+n-1]) - q* * b[n-1] (>=0, <beta). */ | |
| { | |
| mp_twolimb_t c2 = /* c1*beta+r[j+n-2] */ | |
| ((mp_twolimb_t) c1 << GMP_LIMB_BITS) | r_ptr[j + b_len - 2]; | |
| mp_twolimb_t c3 = /* b[n-2] * q* */ | |
| (mp_twolimb_t) b_2msd * (mp_twolimb_t) q_star; | |
| /* While c2 < c3, increase c2 and decrease c3. | |
| Consider c3-c2. While it is > 0, decrease it by | |
| b[n-1]*beta+b[n-2]. Because of b[n-1]*beta+b[n-2] >= beta^2/2 | |
| this can happen only twice. */ | |
| if (c3 > c2) | |
| { | |
| q_star = q_star - 1; /* q* := q* - 1 */ | |
| if (c3 - c2 > b_msdd) | |
| q_star = q_star - 1; /* q* := q* - 1 */ | |
| } | |
| } | |
| if (q_star > 0) | |
| subtract: | |
| { | |
| /* Subtract r := r - b * q* * beta^j. */ | |
| mp_limb_t cr; | |
| { | |
| const mp_limb_t *sourceptr = b_ptr; | |
| mp_limb_t *destptr = r_ptr + j; | |
| mp_twolimb_t carry = 0; | |
| for (size_t count = b_len; count > 0; count--) | |
| { | |
| /* Here 0 <= carry <= q*. */ | |
| carry = | |
| carry | |
| + (mp_twolimb_t) q_star * (mp_twolimb_t) *sourceptr++ | |
| + (mp_limb_t) ~(*destptr); | |
| /* Here 0 <= carry <= beta*q* + beta-1. */ | |
| *destptr++ = ~(mp_limb_t) carry; | |
| carry = carry >> GMP_LIMB_BITS; /* <= q* */ | |
| } | |
| cr = (mp_limb_t) carry; | |
| } | |
| /* Subtract cr from r_ptr[j + b_len], then forget about | |
| r_ptr[j + b_len]. */ | |
| if (cr > r_ptr[j + b_len]) | |
| { | |
| /* Subtraction gave a carry. */ | |
| q_star = q_star - 1; /* q* := q* - 1 */ | |
| /* Add b back. */ | |
| { | |
| const mp_limb_t *sourceptr = b_ptr; | |
| mp_limb_t *destptr = r_ptr + j; | |
| mp_limb_t carry = 0; | |
| for (size_t count = b_len; count > 0; count--) | |
| { | |
| mp_limb_t source1 = *sourceptr++; | |
| mp_limb_t source2 = *destptr; | |
| *destptr++ = source1 + source2 + carry; | |
| carry = | |
| (carry | |
| ? source1 >= (mp_limb_t) ~source2 | |
| : source1 > (mp_limb_t) ~source2); | |
| } | |
| } | |
| /* Forget about the carry and about r[j+n]. */ | |
| } | |
| } | |
| /* q* is determined. Store it as q[j]. */ | |
| q_ptr[j] = q_star; | |
| if (j == 0) | |
| break; | |
| j--; | |
| } | |
| } | |
| r_len = b_len; | |
| /* Normalise q. */ | |
| if (q_ptr[q_len - 1] == 0) | |
| q_len--; | |
| # if 0 /* Not needed here, since we need r only to compare it with b/2, and | |
| b is shifted left by s bits. */ | |
| /* Shift r right by s bits. */ | |
| if (s > 0) | |
| { | |
| mp_limb_t ptr = r_ptr + r_len; | |
| mp_twolimb_t accu = 0; | |
| for (size_t count = r_len; count > 0; count--) | |
| { | |
| accu = (mp_twolimb_t) (mp_limb_t) accu << GMP_LIMB_BITS; | |
| accu += (mp_twolimb_t) *--ptr << (GMP_LIMB_BITS - s); | |
| *ptr = (mp_limb_t) (accu >> GMP_LIMB_BITS); | |
| } | |
| } | |
| # endif | |
| /* Normalise r. */ | |
| while (r_len > 0 && r_ptr[r_len - 1] == 0) | |
| r_len--; | |
| } | |
| /* Compare r << 1 with b. */ | |
| if (r_len > b_len) | |
| goto increment_q; | |
| for (size_t i = b_len;;) | |
| { | |
| mp_limb_t r_i = | |
| (i <= r_len && i > 0 ? r_ptr[i - 1] >> (GMP_LIMB_BITS - 1) : 0) | |
| | (i < r_len ? r_ptr[i] << 1 : 0); | |
| mp_limb_t b_i = (i < b_len ? b_ptr[i] : 0); | |
| if (r_i > b_i) | |
| goto increment_q; | |
| if (r_i < b_i) | |
| goto keep_q; | |
| if (i == 0) | |
| break; | |
| i--; | |
| } | |
| if (q_len > 0 && ((q_ptr[0] & 1) != 0)) | |
| /* q is odd. */ | |
| increment_q: | |
| { | |
| for (size_t i = 0; i < q_len; i++) | |
| if (++(q_ptr[i]) != 0) | |
| goto keep_q; | |
| q_ptr[q_len++] = 1; | |
| } | |
| keep_q: | |
| free (tmp_roomptr); | |
| q->limbs = q_ptr; | |
| q->nlimbs = q_len; | |
| return roomptr; | |
| } | |
| /* Avoid pointless GCC warning "argument 1 value '18446744073709551615' exceeds | |
| maximum object size 9223372036854775807", triggered by the use of xsum as | |
| argument of malloc. */ | |
| # if _GL_GNUC_PREREQ (7, 0) | |
| # pragma GCC diagnostic push | |
| # pragma GCC diagnostic ignored "-Walloc-size-larger-than=" | |
| # endif | |
| /* Convert a bignum a >= 0, multiplied with 10^extra_zeroes, to decimal | |
| representation. | |
| Destroys the contents of a. | |
| Return the allocated memory - containing the decimal digits in low-to-high | |
| order, terminated with a NUL character - in case of success, NULL in case | |
| of memory allocation failure. */ | |
| static char * | |
| convert_to_decimal (mpn_t a, size_t extra_zeroes) | |
| { | |
| mp_limb_t *a_ptr = a.limbs; | |
| size_t a_len = a.nlimbs; | |
| /* 0.03345 is slightly larger than log(2)/(9*log(10)). */ | |
| size_t c_len = 9 * ((size_t)(a_len * (GMP_LIMB_BITS * 0.03345f)) + 1); | |
| /* We need extra_zeroes bytes for zeroes, followed by c_len bytes for the | |
| digits of a, followed by 1 byte for the terminating NUL. */ | |
| char *c_ptr = (char *) malloc (xsum (xsum (extra_zeroes, c_len), 1)); | |
| if (c_ptr != NULL) | |
| { | |
| char *d_ptr = c_ptr; | |
| for (; extra_zeroes > 0; extra_zeroes--) | |
| *d_ptr++ = '0'; | |
| while (a_len > 0) | |
| { | |
| /* Divide a by 10^9, in-place. */ | |
| mp_limb_t remainder = 0; | |
| mp_limb_t *ptr = a_ptr + a_len; | |
| for (size_t count = a_len; count > 0; count--) | |
| { | |
| mp_twolimb_t num = | |
| ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--ptr; | |
| *ptr = num / 1000000000; | |
| remainder = num % 1000000000; | |
| } | |
| /* Store the remainder as 9 decimal digits. */ | |
| for (size_t count = 9; count > 0; count--) | |
| { | |
| *d_ptr++ = '0' + (remainder % 10); | |
| remainder = remainder / 10; | |
| } | |
| /* Normalize a. */ | |
| if (a_ptr[a_len - 1] == 0) | |
| a_len--; | |
| } | |
| /* Remove leading zeroes. */ | |
| while (d_ptr > c_ptr && d_ptr[-1] == '0') | |
| d_ptr--; | |
| /* But keep at least one zero. */ | |
| if (d_ptr == c_ptr) | |
| *d_ptr++ = '0'; | |
| /* Terminate the string. */ | |
| *d_ptr = '\0'; | |
| } | |
| return c_ptr; | |
| } | |
| # if _GL_GNUC_PREREQ (7, 0) | |
| # pragma GCC diagnostic pop | |
| # endif | |
| # if NEED_PRINTF_LONG_DOUBLE | |
| /* Assuming x is finite and >= 0: | |
| write x as x = 2^e * m, where m is a bignum. | |
| Return the allocated memory in case of success, NULL in case of memory | |
| allocation failure. */ | |
| static void * | |
| decode_long_double (long double x, int *ep, mpn_t *mp) | |
| { | |
| /* Allocate memory for result. */ | |
| mpn_t m; | |
| m.nlimbs = (LDBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; | |
| m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t)); | |
| if (m.limbs == NULL) | |
| return NULL; | |
| /* Split into exponential part and mantissa. */ | |
| int exp; | |
| long double y = safe_frexpl (x, &exp); | |
| if (!(y >= 0.0L && y < 1.0L)) | |
| abort (); | |
| /* x = 2^exp * y = 2^(exp - LDBL_MANT_BIT) * (y * 2^LDBL_MANT_BIT), and the | |
| latter is an integer. */ | |
| /* Convert the mantissa (y * 2^LDBL_MANT_BIT) to a sequence of limbs. | |
| I'm not sure whether it's safe to cast a 'long double' value between | |
| 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only | |
| 'long double' values between 0 and 2^16 (to 'unsigned int' or 'int', | |
| doesn't matter). */ | |
| # if (LDBL_MANT_BIT % GMP_LIMB_BITS) != 0 | |
| # if (LDBL_MANT_BIT % GMP_LIMB_BITS) > GMP_LIMB_BITS / 2 | |
| { | |
| mp_limb_t hi, lo; | |
| y *= (mp_limb_t) 1 << (LDBL_MANT_BIT % (GMP_LIMB_BITS / 2)); | |
| hi = (int) y; | |
| y -= hi; | |
| if (!(y >= 0.0L && y < 1.0L)) | |
| abort (); | |
| y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); | |
| lo = (int) y; | |
| y -= lo; | |
| if (!(y >= 0.0L && y < 1.0L)) | |
| abort (); | |
| m.limbs[LDBL_MANT_BIT / GMP_LIMB_BITS] = (hi << (GMP_LIMB_BITS / 2)) | lo; | |
| } | |
| # else | |
| { | |
| mp_limb_t d; | |
| y *= (mp_limb_t) 1 << (LDBL_MANT_BIT % GMP_LIMB_BITS); | |
| d = (int) y; | |
| y -= d; | |
| if (!(y >= 0.0L && y < 1.0L)) | |
| abort (); | |
| m.limbs[LDBL_MANT_BIT / GMP_LIMB_BITS] = d; | |
| } | |
| # endif | |
| # endif | |
| for (size_t i = LDBL_MANT_BIT / GMP_LIMB_BITS; i > 0; ) | |
| { | |
| mp_limb_t hi, lo; | |
| y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); | |
| hi = (int) y; | |
| y -= hi; | |
| if (!(y >= 0.0L && y < 1.0L)) | |
| abort (); | |
| y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); | |
| lo = (int) y; | |
| y -= lo; | |
| if (!(y >= 0.0L && y < 1.0L)) | |
| abort (); | |
| m.limbs[--i] = (hi << (GMP_LIMB_BITS / 2)) | lo; | |
| } | |
| # if 0 /* On FreeBSD 6.1/x86, 'long double' numbers sometimes have excess | |
| precision. */ | |
| if (!(y == 0.0L)) | |
| abort (); | |
| # endif | |
| /* Normalise. */ | |
| while (m.nlimbs > 0 && m.limbs[m.nlimbs - 1] == 0) | |
| m.nlimbs--; | |
| *mp = m; | |
| *ep = exp - LDBL_MANT_BIT; | |
| return m.limbs; | |
| } | |
| # endif | |
| # if NEED_PRINTF_DOUBLE | |
| /* Assuming x is finite and >= 0: | |
| write x as x = 2^e * m, where m is a bignum. | |
| Return the allocated memory in case of success, NULL in case of memory | |
| allocation failure. */ | |
| static void * | |
| decode_double (double x, int *ep, mpn_t *mp) | |
| { | |
| /* Allocate memory for result. */ | |
| mpn_t m; | |
| m.nlimbs = (DBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; | |
| m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t)); | |
| if (m.limbs == NULL) | |
| return NULL; | |
| /* Split into exponential part and mantissa. */ | |
| int exp; | |
| double y = frexp (x, &exp); | |
| if (!(y >= 0.0 && y < 1.0)) | |
| abort (); | |
| /* x = 2^exp * y = 2^(exp - DBL_MANT_BIT) * (y * 2^DBL_MANT_BIT), and the | |
| latter is an integer. */ | |
| /* Convert the mantissa (y * 2^DBL_MANT_BIT) to a sequence of limbs. | |
| I'm not sure whether it's safe to cast a 'double' value between | |
| 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only | |
| 'double' values between 0 and 2^16 (to 'unsigned int' or 'int', | |
| doesn't matter). */ | |
| # if (DBL_MANT_BIT % GMP_LIMB_BITS) != 0 | |
| # if (DBL_MANT_BIT % GMP_LIMB_BITS) > GMP_LIMB_BITS / 2 | |
| { | |
| mp_limb_t hi, lo; | |
| y *= (mp_limb_t) 1 << (DBL_MANT_BIT % (GMP_LIMB_BITS / 2)); | |
| hi = (int) y; | |
| y -= hi; | |
| if (!(y >= 0.0 && y < 1.0)) | |
| abort (); | |
| y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); | |
| lo = (int) y; | |
| y -= lo; | |
| if (!(y >= 0.0 && y < 1.0)) | |
| abort (); | |
| m.limbs[DBL_MANT_BIT / GMP_LIMB_BITS] = (hi << (GMP_LIMB_BITS / 2)) | lo; | |
| } | |
| # else | |
| { | |
| mp_limb_t d; | |
| y *= (mp_limb_t) 1 << (DBL_MANT_BIT % GMP_LIMB_BITS); | |
| d = (int) y; | |
| y -= d; | |
| if (!(y >= 0.0 && y < 1.0)) | |
| abort (); | |
| m.limbs[DBL_MANT_BIT / GMP_LIMB_BITS] = d; | |
| } | |
| # endif | |
| # endif | |
| for (size_t i = DBL_MANT_BIT / GMP_LIMB_BITS; i > 0; ) | |
| { | |
| mp_limb_t hi, lo; | |
| y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); | |
| hi = (int) y; | |
| y -= hi; | |
| if (!(y >= 0.0 && y < 1.0)) | |
| abort (); | |
| y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); | |
| lo = (int) y; | |
| y -= lo; | |
| if (!(y >= 0.0 && y < 1.0)) | |
| abort (); | |
| m.limbs[--i] = (hi << (GMP_LIMB_BITS / 2)) | lo; | |
| } | |
| if (!(y == 0.0)) | |
| abort (); | |
| /* Normalise. */ | |
| while (m.nlimbs > 0 && m.limbs[m.nlimbs - 1] == 0) | |
| m.nlimbs--; | |
| *mp = m; | |
| *ep = exp - DBL_MANT_BIT; | |
| return m.limbs; | |
| } | |
| # endif | |
| /* Assuming x = 2^e * m is finite and >= 0, and n is an integer: | |
| Returns the decimal representation of round (x * 10^n). | |
| Return the allocated memory - containing the decimal digits in low-to-high | |
| order, terminated with a NUL character - in case of success, NULL in case | |
| of memory allocation failure. */ | |
| static char * | |
| scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) | |
| { | |
| /* x = 2^e * m, hence | |
| y = round (2^e * 10^n * m) = round (2^(e+n) * 5^n * m) | |
| = round (2^s * 5^n * m). */ | |
| int s = e + n; | |
| size_t extra_zeroes = 0; | |
| /* Factor out a common power of 10 if possible. */ | |
| if (s > 0 && n > 0) | |
| { | |
| extra_zeroes = (s < n ? s : n); | |
| s -= extra_zeroes; | |
| n -= extra_zeroes; | |
| } | |
| /* Here y = round (2^s * 5^n * m) * 10^extra_zeroes. | |
| Before converting to decimal, we need to compute | |
| z = round (2^s * 5^n * m). */ | |
| /* Compute 5^|n|, possibly shifted by |s| bits if n and s have the same | |
| sign. 2.322 is slightly larger than log(5)/log(2). */ | |
| unsigned int abs_n = (n >= 0 ? n : -n); | |
| unsigned int abs_s = (s >= 0 ? s : -s); | |
| mp_limb_t *pow5_ptr = | |
| (mp_limb_t *) malloc (((int)(abs_n * (2.322f / GMP_LIMB_BITS)) + 1 | |
| + abs_s / GMP_LIMB_BITS + 1) | |
| * sizeof (mp_limb_t)); | |
| if (pow5_ptr == NULL) | |
| { | |
| free (memory); | |
| return NULL; | |
| } | |
| /* Initialize with 1. */ | |
| pow5_ptr[0] = 1; | |
| size_t pow5_len = 1; | |
| /* Multiply with 5^|n|. */ | |
| if (abs_n > 0) | |
| { | |
| static mp_limb_t const small_pow5[13 + 1] = | |
| { | |
| 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, | |
| 48828125, 244140625, 1220703125 | |
| }; | |
| for (unsigned int n13 = 0; n13 <= abs_n; n13 += 13) | |
| { | |
| mp_limb_t digit1 = small_pow5[n13 + 13 <= abs_n ? 13 : abs_n - n13]; | |
| mp_twolimb_t carry = 0; | |
| for (size_t j = 0; j < pow5_len; j++) | |
| { | |
| mp_limb_t digit2 = pow5_ptr[j]; | |
| carry += (mp_twolimb_t) digit1 * (mp_twolimb_t) digit2; | |
| pow5_ptr[j] = (mp_limb_t) carry; | |
| carry = carry >> GMP_LIMB_BITS; | |
| } | |
| if (carry > 0) | |
| pow5_ptr[pow5_len++] = (mp_limb_t) carry; | |
| } | |
| } | |
| unsigned int s_limbs = abs_s / GMP_LIMB_BITS; | |
| unsigned int s_bits = abs_s % GMP_LIMB_BITS; | |
| mpn_t pow5; | |
| mpn_t z; | |
| void *z_memory; | |
| if (n >= 0 ? s >= 0 : s <= 0) | |
| { | |
| /* Multiply with 2^|s|. */ | |
| if (s_bits > 0) | |
| { | |
| mp_limb_t *ptr = pow5_ptr; | |
| mp_twolimb_t accu = 0; | |
| for (size_t count = pow5_len; count > 0; count--) | |
| { | |
| accu += (mp_twolimb_t) *ptr << s_bits; | |
| *ptr++ = (mp_limb_t) accu; | |
| accu = accu >> GMP_LIMB_BITS; | |
| } | |
| if (accu > 0) | |
| { | |
| *ptr = (mp_limb_t) accu; | |
| pow5_len++; | |
| } | |
| } | |
| if (s_limbs > 0) | |
| { | |
| for (size_t count = pow5_len; count > 0;) | |
| { | |
| count--; | |
| pow5_ptr[s_limbs + count] = pow5_ptr[count]; | |
| } | |
| for (size_t count = s_limbs; count > 0;) | |
| { | |
| count--; | |
| pow5_ptr[count] = 0; | |
| } | |
| pow5_len += s_limbs; | |
| } | |
| pow5.limbs = pow5_ptr; | |
| pow5.nlimbs = pow5_len; | |
| if (n >= 0) | |
| { | |
| /* Multiply m with pow5. No division needed. */ | |
| z_memory = multiply (m, pow5, &z); | |
| } | |
| else | |
| { | |
| /* Divide m by pow5 and round. */ | |
| z_memory = divide (m, pow5, &z); | |
| } | |
| } | |
| else | |
| { | |
| pow5.limbs = pow5_ptr; | |
| pow5.nlimbs = pow5_len; | |
| if (n >= 0) | |
| { | |
| /* n >= 0, s < 0. | |
| Multiply m with pow5, then divide by 2^|s|. */ | |
| mpn_t numerator; | |
| mpn_t denominator; | |
| void *tmp_memory; | |
| tmp_memory = multiply (m, pow5, &numerator); | |
| if (tmp_memory == NOMEM_PTR) | |
| { | |
| free (pow5_ptr); | |
| free (memory); | |
| return NULL; | |
| } | |
| /* Construct 2^|s|. */ | |
| { | |
| mp_limb_t *ptr = pow5_ptr + pow5_len; | |
| for (size_t i = 0; i < s_limbs; i++) | |
| ptr[i] = 0; | |
| ptr[s_limbs] = (mp_limb_t) 1 << s_bits; | |
| denominator.limbs = ptr; | |
| denominator.nlimbs = s_limbs + 1; | |
| } | |
| z_memory = divide (numerator, denominator, &z); | |
| free (tmp_memory); | |
| } | |
| else | |
| { | |
| /* n < 0, s > 0. | |
| Multiply m with 2^s, then divide by pow5. */ | |
| mpn_t numerator; | |
| mp_limb_t *num_ptr; | |
| num_ptr = (mp_limb_t *) malloc ((m.nlimbs + s_limbs + 1) | |
| * sizeof (mp_limb_t)); | |
| if (num_ptr == NULL) | |
| { | |
| free (pow5_ptr); | |
| free (memory); | |
| return NULL; | |
| } | |
| { | |
| mp_limb_t *destptr = num_ptr; | |
| for (size_t i = 0; i < s_limbs; i++) | |
| *destptr++ = 0; | |
| if (s_bits > 0) | |
| { | |
| const mp_limb_t *sourceptr = m.limbs; | |
| mp_twolimb_t accu = 0; | |
| for (size_t count = m.nlimbs; count > 0; count--) | |
| { | |
| accu += (mp_twolimb_t) *sourceptr++ << s_bits; | |
| *destptr++ = (mp_limb_t) accu; | |
| accu = accu >> GMP_LIMB_BITS; | |
| } | |
| if (accu > 0) | |
| *destptr++ = (mp_limb_t) accu; | |
| } | |
| else | |
| { | |
| const mp_limb_t *sourceptr = m.limbs; | |
| for (size_t count = m.nlimbs; count > 0; count--) | |
| *destptr++ = *sourceptr++; | |
| } | |
| numerator.limbs = num_ptr; | |
| numerator.nlimbs = destptr - num_ptr; | |
| } | |
| z_memory = divide (numerator, pow5, &z); | |
| free (num_ptr); | |
| } | |
| } | |
| free (pow5_ptr); | |
| free (memory); | |
| /* Here y = round (x * 10^n) = z * 10^extra_zeroes. */ | |
| if (z_memory == NOMEM_PTR) | |
| return NULL; | |
| char *digits = convert_to_decimal (z, extra_zeroes); | |
| free (z_memory); | |
| return digits; | |
| } | |
| # if NEED_PRINTF_LONG_DOUBLE | |
| /* Assuming x is finite and >= 0, and n is an integer: | |
| Returns the decimal representation of round (x * 10^n). | |
| Return the allocated memory - containing the decimal digits in low-to-high | |
| order, terminated with a NUL character - in case of success, NULL in case | |
| of memory allocation failure. */ | |
| static char * | |
| scale10_round_decimal_long_double (long double x, int n) | |
| { | |
| int e; | |
| mpn_t m; | |
| void *memory = decode_long_double (x, &e, &m); | |
| if (memory != NULL) | |
| return scale10_round_decimal_decoded (e, m, memory, n); | |
| else | |
| return NULL; | |
| } | |
| # endif | |
| # if NEED_PRINTF_DOUBLE | |
| /* Assuming x is finite and >= 0, and n is an integer: | |
| Returns the decimal representation of round (x * 10^n). | |
| Return the allocated memory - containing the decimal digits in low-to-high | |
| order, terminated with a NUL character - in case of success, NULL in case | |
| of memory allocation failure. */ | |
| static char * | |
| scale10_round_decimal_double (double x, int n) | |
| { | |
| int e; | |
| mpn_t m; | |
| void *memory = decode_double (x, &e, &m); | |
| if (memory != NULL) | |
| return scale10_round_decimal_decoded (e, m, memory, n); | |
| else | |
| return NULL; | |
| } | |
| # endif | |
| # if NEED_PRINTF_LONG_DOUBLE | |
| /* Assuming x is finite and > 0: | |
| Return an approximation for n with 10^n <= x < 10^(n+1). | |
| The approximation is usually the right n, but may be off by 1 sometimes. */ | |
| static int | |
| floorlog10l (long double x) | |
| { | |
| /* Split into exponential part and mantissa. */ | |
| int exp; | |
| long double y = safe_frexpl (x, &exp); | |
| if (!(y >= 0.0L && y < 1.0L)) | |
| abort (); | |
| if (y == 0.0L) | |
| return INT_MIN; | |
| if (y < 0.5L) | |
| { | |
| while (y < (1.0L / (1 << (GMP_LIMB_BITS / 2)) / (1 << (GMP_LIMB_BITS / 2)))) | |
| { | |
| y *= 1.0L * (1 << (GMP_LIMB_BITS / 2)) * (1 << (GMP_LIMB_BITS / 2)); | |
| exp -= GMP_LIMB_BITS; | |
| } | |
| if (y < (1.0L / (1 << 16))) | |
| { | |
| y *= 1.0L * (1 << 16); | |
| exp -= 16; | |
| } | |
| if (y < (1.0L / (1 << 8))) | |
| { | |
| y *= 1.0L * (1 << 8); | |
| exp -= 8; | |
| } | |
| if (y < (1.0L / (1 << 4))) | |
| { | |
| y *= 1.0L * (1 << 4); | |
| exp -= 4; | |
| } | |
| if (y < (1.0L / (1 << 2))) | |
| { | |
| y *= 1.0L * (1 << 2); | |
| exp -= 2; | |
| } | |
| if (y < (1.0L / (1 << 1))) | |
| { | |
| y *= 1.0L * (1 << 1); | |
| exp -= 1; | |
| } | |
| } | |
| if (!(y >= 0.5L && y < 1.0L)) | |
| abort (); | |
| /* Compute an approximation for l = log2(x) = exp + log2(y). */ | |
| double l = exp; | |
| double z = y; | |
| if (z < 0.70710678118654752444) | |
| { | |
| z *= 1.4142135623730950488; | |
| l -= 0.5; | |
| } | |
| if (z < 0.8408964152537145431) | |
| { | |
| z *= 1.1892071150027210667; | |
| l -= 0.25; | |
| } | |
| if (z < 0.91700404320467123175) | |
| { | |
| z *= 1.0905077326652576592; | |
| l -= 0.125; | |
| } | |
| if (z < 0.9576032806985736469) | |
| { | |
| z *= 1.0442737824274138403; | |
| l -= 0.0625; | |
| } | |
| /* Now 0.95 <= z <= 1.01. */ | |
| z = 1 - z; | |
| /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...) | |
| Four terms are enough to get an approximation with error < 10^-7. */ | |
| l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); | |
| /* Finally multiply with log(2)/log(10), yields an approximation for | |
| log10(x). */ | |
| l *= 0.30102999566398119523; | |
| /* Round down to the next integer. */ | |
| return (int) l + (l < 0 ? -1 : 0); | |
| } | |
| # endif | |
| # if NEED_PRINTF_DOUBLE | |
| /* Assuming x is finite and > 0: | |
| Return an approximation for n with 10^n <= x < 10^(n+1). | |
| The approximation is usually the right n, but may be off by 1 sometimes. */ | |
| static int | |
| floorlog10 (double x) | |
| { | |
| /* Split into exponential part and mantissa. */ | |
| int exp; | |
| double y = frexp (x, &exp); | |
| if (!(y >= 0.0 && y < 1.0)) | |
| abort (); | |
| if (y == 0.0) | |
| return INT_MIN; | |
| if (y < 0.5) | |
| { | |
| while (y < (1.0 / (1 << (GMP_LIMB_BITS / 2)) / (1 << (GMP_LIMB_BITS / 2)))) | |
| { | |
| y *= 1.0 * (1 << (GMP_LIMB_BITS / 2)) * (1 << (GMP_LIMB_BITS / 2)); | |
| exp -= GMP_LIMB_BITS; | |
| } | |
| if (y < (1.0 / (1 << 16))) | |
| { | |
| y *= 1.0 * (1 << 16); | |
| exp -= 16; | |
| } | |
| if (y < (1.0 / (1 << 8))) | |
| { | |
| y *= 1.0 * (1 << 8); | |
| exp -= 8; | |
| } | |
| if (y < (1.0 / (1 << 4))) | |
| { | |
| y *= 1.0 * (1 << 4); | |
| exp -= 4; | |
| } | |
| if (y < (1.0 / (1 << 2))) | |
| { | |
| y *= 1.0 * (1 << 2); | |
| exp -= 2; | |
| } | |
| if (y < (1.0 / (1 << 1))) | |
| { | |
| y *= 1.0 * (1 << 1); | |
| exp -= 1; | |
| } | |
| } | |
| if (!(y >= 0.5 && y < 1.0)) | |
| abort (); | |
| /* Compute an approximation for l = log2(x) = exp + log2(y). */ | |
| double l = exp; | |
| double z = y; | |
| if (z < 0.70710678118654752444) | |
| { | |
| z *= 1.4142135623730950488; | |
| l -= 0.5; | |
| } | |
| if (z < 0.8408964152537145431) | |
| { | |
| z *= 1.1892071150027210667; | |
| l -= 0.25; | |
| } | |
| if (z < 0.91700404320467123175) | |
| { | |
| z *= 1.0905077326652576592; | |
| l -= 0.125; | |
| } | |
| if (z < 0.9576032806985736469) | |
| { | |
| z *= 1.0442737824274138403; | |
| l -= 0.0625; | |
| } | |
| /* Now 0.95 <= z <= 1.01. */ | |
| z = 1 - z; | |
| /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...) | |
| Four terms are enough to get an approximation with error < 10^-7. */ | |
| l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); | |
| /* Finally multiply with log(2)/log(10), yields an approximation for | |
| log10(x). */ | |
| l *= 0.30102999566398119523; | |
| /* Round down to the next integer. */ | |
| return (int) l + (l < 0 ? -1 : 0); | |
| } | |
| # endif | |
| /* Tests whether a string of digits consists of exactly PRECISION zeroes and | |
| a single '1' digit. */ | |
| static int | |
| is_borderline (const char *digits, size_t precision) | |
| { | |
| for (; precision > 0; precision--, digits++) | |
| if (*digits != '0') | |
| return 0; | |
| if (*digits != '1') | |
| return 0; | |
| digits++; | |
| return *digits == '\0'; | |
| } | |
| #endif | |
| #if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF | |
| /* Use a different function name, to make it possible that the 'wchar_t' | |
| parametrization and the 'char' parametrization get compiled in the same | |
| translation unit. */ | |
| # if WIDE_CHAR_VERSION | |
| # define MAX_ROOM_NEEDED wmax_room_needed | |
| # else | |
| # define MAX_ROOM_NEEDED max_room_needed | |
| # endif | |
| /* Returns the number of TCHAR_T units needed as temporary space for the result | |
| of sprintf or SNPRINTF of a single conversion directive. */ | |
| static size_t | |
| MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, | |
| arg_type type, int flags, size_t width, int has_precision, | |
| size_t precision, int pad_ourselves) | |
| { | |
| size_t tmp_length; | |
| switch (conversion) | |
| { | |
| case 'd': case 'i': case 'u': | |
| switch (type) | |
| { | |
| default: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned int) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_LONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (long int) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long int) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_LONGLONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (long long int) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGLONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_INT8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (int8_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint8_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_INT16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (int16_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint16_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_INT32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (int32_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint32_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_INT64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (int64_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint64_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_INT_FAST8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (int_fast8_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_INT_FAST16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (int_fast16_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_INT_FAST32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (int_fast32_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_INT_FAST64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (int_fast64_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT | |
| * 0.30103 /* binary -> decimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| } | |
| if (tmp_length < precision) | |
| tmp_length = precision; | |
| /* Account for thousands separators. */ | |
| if (flags & FLAG_GROUP) | |
| { | |
| /* A thousands separator needs to be inserted at most every 2 digits. | |
| This is the case in the ta_IN locale. */ | |
| # if WIDE_CHAR_VERSION | |
| tmp_length = xsum (tmp_length, tmp_length / 2 * THOUSEP_WCHAR_MAXLEN); | |
| # else | |
| tmp_length = xsum (tmp_length, tmp_length / 2 * THOUSEP_CHAR_MAXLEN); | |
| # endif | |
| } | |
| /* Add 1, to account for a leading sign. */ | |
| tmp_length = xsum (tmp_length, 1); | |
| break; | |
| case 'b': | |
| #if SUPPORT_GNU_PRINTF_DIRECTIVES \ | |
| || (__GLIBC__ + (__GLIBC_MINOR__ >= 35) > 2) | |
| case 'B': | |
| #endif | |
| switch (type) | |
| { | |
| default: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned int) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long int) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGLONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint8_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint16_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint32_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint64_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| } | |
| if (tmp_length < precision) | |
| tmp_length = precision; | |
| /* Add 2, to account for a prefix from the alternate form. */ | |
| tmp_length = xsum (tmp_length, 2); | |
| break; | |
| case 'o': | |
| switch (type) | |
| { | |
| default: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned int) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long int) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGLONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint8_t) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint16_t) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint32_t) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint64_t) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT | |
| * 0.333334 /* binary -> octal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| } | |
| if (tmp_length < precision) | |
| tmp_length = precision; | |
| /* Add 1, to account for a leading sign. */ | |
| tmp_length = xsum (tmp_length, 1); | |
| break; | |
| case 'x': case 'X': | |
| switch (type) | |
| { | |
| default: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned int) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long int) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGLONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint8_t) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint16_t) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint32_t) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint64_t) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| } | |
| if (tmp_length < precision) | |
| tmp_length = precision; | |
| /* Add 2, to account for a prefix from the alternate form. */ | |
| tmp_length = xsum (tmp_length, 2); | |
| break; | |
| case 'e': case 'E': | |
| tmp_length = | |
| 12; /* sign, decimal point, exponent etc. */ | |
| tmp_length = xsum (tmp_length, precision); | |
| break; | |
| case 'f': case 'F': | |
| if (type == TYPE_LONGDOUBLE) | |
| tmp_length = | |
| (unsigned int) (LDBL_MAX_EXP | |
| * 0.30103 /* binary -> decimal */ | |
| * 0.5 * 3 /* estimate for FLAG_GROUP */ | |
| ) | |
| + 1 /* turn floor into ceil */ | |
| + 10; /* sign, decimal point etc. */ | |
| else | |
| tmp_length = | |
| (unsigned int) (DBL_MAX_EXP | |
| * 0.30103 /* binary -> decimal */ | |
| * 0.5 * 3 /* estimate for FLAG_GROUP */ | |
| ) | |
| + 1 /* turn floor into ceil */ | |
| + 10; /* sign, decimal point etc. */ | |
| tmp_length = xsum (tmp_length, precision); | |
| break; | |
| case 'g': case 'G': | |
| tmp_length = | |
| 12; /* sign, decimal point, exponent etc. */ | |
| tmp_length = xsum (tmp_length, | |
| precision | |
| * 0.5 * 3 /* estimate for FLAG_GROUP */ | |
| ); | |
| break; | |
| case 'a': case 'A': | |
| if (type == TYPE_LONGDOUBLE) | |
| tmp_length = | |
| (unsigned int) (LDBL_DIG | |
| * 0.831 /* decimal -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| else | |
| tmp_length = | |
| (unsigned int) (DBL_DIG | |
| * 0.831 /* decimal -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| if (tmp_length < precision) | |
| tmp_length = precision; | |
| /* Account for sign, decimal point etc. */ | |
| tmp_length = xsum (tmp_length, 12); | |
| break; | |
| case 'c': | |
| # if HAVE_WINT_T && !WIDE_CHAR_VERSION | |
| if (type == TYPE_WIDE_CHAR) | |
| { | |
| tmp_length = MB_CUR_MAX; | |
| # if ENABLE_WCHAR_FALLBACK | |
| if (tmp_length < (sizeof (wchar_t) > 2 ? 10 : 6)) | |
| tmp_length = (sizeof (wchar_t) > 2 ? 10 : 6); | |
| # endif | |
| } | |
| else | |
| # endif | |
| tmp_length = 1; | |
| break; | |
| case 's': | |
| if (type == TYPE_WIDE_STRING) | |
| { | |
| # if WIDE_CHAR_VERSION | |
| /* ISO C says about %ls in fwprintf: | |
| "If the precision is not specified or is greater than the size | |
| of the array, the array shall contain a null wide character." | |
| So if there is a precision, we must not use wcslen. */ | |
| const wchar_t *arg = ap->arg[arg_index].a.a_wide_string; | |
| if (has_precision) | |
| tmp_length = local_wcsnlen (arg, precision); | |
| else | |
| tmp_length = local_wcslen (arg); | |
| # else | |
| /* ISO C says about %ls in fprintf: | |
| "If a precision is specified, no more than that many bytes are | |
| written (including shift sequences, if any), and the array | |
| shall contain a null wide character if, to equal the multibyte | |
| character sequence length given by the precision, the function | |
| would need to access a wide character one past the end of the | |
| array." | |
| So if there is a precision, we must not use wcslen. */ | |
| /* This case has already been handled separately in VASNPRINTF. */ | |
| abort (); | |
| # endif | |
| } | |
| else | |
| { | |
| # if WIDE_CHAR_VERSION | |
| /* ISO C says about %s in fwprintf: | |
| "If the precision is not specified or is greater than the size | |
| of the converted array, the converted array shall contain a | |
| null wide character." | |
| So if there is a precision, we must not use strlen. */ | |
| /* This case has already been handled separately in VASNPRINTF. */ | |
| abort (); | |
| # else | |
| /* ISO C says about %s in fprintf: | |
| "If the precision is not specified or greater than the size of | |
| the array, the array shall contain a null character." | |
| So if there is a precision, we must not use strlen. */ | |
| const char *arg = ap->arg[arg_index].a.a_string; | |
| if (has_precision) | |
| tmp_length = local_strnlen (arg, precision); | |
| else | |
| tmp_length = strlen (arg); | |
| # endif | |
| } | |
| break; | |
| case 'p': | |
| tmp_length = | |
| (unsigned int) (sizeof (void *) * CHAR_BIT | |
| * 0.25 /* binary -> hexadecimal */ | |
| ) | |
| + 1 /* turn floor into ceil */ | |
| + 2; /* account for leading 0x */ | |
| break; | |
| default: | |
| abort (); | |
| } | |
| if (!pad_ourselves) | |
| { | |
| # if ENABLE_UNISTDIO | |
| /* Padding considers the number of characters, therefore the number of | |
| elements after padding may be | |
| > max (tmp_length, width) | |
| but is certainly | |
| <= tmp_length + width. */ | |
| tmp_length = xsum (tmp_length, width); | |
| # else | |
| /* Padding considers the number of elements, says POSIX. */ | |
| if (tmp_length < width) | |
| tmp_length = width; | |
| # endif | |
| } | |
| tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ | |
| return tmp_length; | |
| } | |
| #endif | |
| DCHAR_T * | |
| VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, | |
| const FCHAR_T *format, va_list args) | |
| { | |
| DIRECTIVES d; | |
| arguments a; | |
| if (PRINTF_PARSE (format, &d, &a) < 0) | |
| /* errno is already set. */ | |
| return NULL; | |
| /* Frees the memory allocated by this function. Preserves errno. */ | |
| #define CLEANUP() \ | |
| if (d.dir != d.direct_alloc_dir) \ | |
| free (d.dir); \ | |
| if (a.arg != a.direct_alloc_arg) \ | |
| free (a.arg); | |
| if (PRINTF_FETCHARGS (args, &a) < 0) | |
| goto fail_1_with_EINVAL; | |
| { | |
| /* Allocate a small buffer that will hold a directive passed to | |
| sprintf or snprintf. */ | |
| size_t buf_neededlength = | |
| xsum4 (7, d.max_width_length, d.max_precision_length, 6); | |
| TCHAR_T *buf; | |
| TCHAR_T *buf_malloced; | |
| #if HAVE_ALLOCA | |
| if (buf_neededlength < 4000 / sizeof (TCHAR_T)) | |
| { | |
| buf = (TCHAR_T *) alloca (buf_neededlength * sizeof (TCHAR_T)); | |
| buf_malloced = NULL; | |
| } | |
| else | |
| #endif | |
| { | |
| size_t buf_memsize = xtimes (buf_neededlength, sizeof (TCHAR_T)); | |
| if (size_overflow_p (buf_memsize)) | |
| goto out_of_memory_1; | |
| buf = (TCHAR_T *) malloc (buf_memsize); | |
| if (buf == NULL) | |
| goto out_of_memory_1; | |
| buf_malloced = buf; | |
| } | |
| /* Output string accumulator. */ | |
| DCHAR_T *result = resultbuf; | |
| size_t allocated = (resultbuf != NULL ? *lengthp : 0); | |
| size_t length = 0; | |
| /* Invariants: | |
| result is either == resultbuf or malloc-allocated. | |
| If result == NULL, resultbuf is == NULL as well. | |
| If length > 0, then result != NULL. */ | |
| /* Ensures that allocated >= needed. Aborts through a jump to | |
| out_of_memory if needed is SIZE_MAX or otherwise too big. */ | |
| #define ENSURE_ALLOCATION_ELSE(needed, oom_statement) \ | |
| if ((needed) > allocated) \ | |
| { \ | |
| allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ | |
| if ((needed) > allocated) \ | |
| allocated = (needed); \ | |
| size_t memory_size = xtimes (allocated, sizeof (DCHAR_T)); \ | |
| if (size_overflow_p (memory_size)) \ | |
| oom_statement \ | |
| DCHAR_T *memory; \ | |
| if (result == resultbuf) \ | |
| memory = (DCHAR_T *) malloc (memory_size); \ | |
| else \ | |
| memory = (DCHAR_T *) realloc (result, memory_size); \ | |
| if (memory == NULL) \ | |
| oom_statement \ | |
| if (result == resultbuf && length > 0) \ | |
| DCHAR_CPY (memory, result, length); \ | |
| result = memory; \ | |
| } | |
| #define ENSURE_ALLOCATION(needed) \ | |
| ENSURE_ALLOCATION_ELSE((needed), goto out_of_memory; ) | |
| const FCHAR_T *cp; | |
| size_t di; | |
| DIRECTIVE *dp; | |
| for (cp = format, di = 0, dp = &d.dir[0]; ; cp = dp->dir_end, di++, dp++) | |
| { | |
| if (cp != dp->dir_start) | |
| { | |
| size_t n = dp->dir_start - cp; | |
| size_t augmented_length = xsum (length, n); | |
| ENSURE_ALLOCATION (augmented_length); | |
| /* This copies a piece of FCHAR_T[] into a DCHAR_T[]. Here we | |
| need that the format string contains only ASCII characters | |
| if FCHAR_T and DCHAR_T are not the same type. */ | |
| if (sizeof (FCHAR_T) == sizeof (DCHAR_T)) | |
| { | |
| DCHAR_CPY (result + length, (const DCHAR_T *) cp, n); | |
| length = augmented_length; | |
| } | |
| else | |
| { | |
| do | |
| result[length++] = *cp++; | |
| while (--n > 0); | |
| } | |
| } | |
| if (di == d.count) | |
| break; | |
| /* Execute a single directive. */ | |
| if (dp->conversion == '%') | |
| { | |
| size_t augmented_length; | |
| if (!(dp->arg_index == ARG_NONE)) | |
| abort (); | |
| augmented_length = xsum (length, 1); | |
| ENSURE_ALLOCATION (augmented_length); | |
| result[length] = '%'; | |
| length = augmented_length; | |
| } | |
| else | |
| { | |
| if (!(dp->arg_index != ARG_NONE)) | |
| abort (); | |
| if (dp->conversion == 'n') | |
| { | |
| #if NEED_PRINTF_WITH_N_DIRECTIVE | |
| switch (a.arg[dp->arg_index].type) | |
| { | |
| case TYPE_COUNT_SCHAR_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_schar_pointer = length; | |
| break; | |
| case TYPE_COUNT_SHORT_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_short_pointer = length; | |
| break; | |
| case TYPE_COUNT_INT_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_int_pointer = length; | |
| break; | |
| case TYPE_COUNT_LONGINT_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_longint_pointer = length; | |
| break; | |
| case TYPE_COUNT_LONGLONGINT_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; | |
| break; | |
| case TYPE_COUNT_INT8_T_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_int8_t_pointer = length; | |
| break; | |
| case TYPE_COUNT_INT16_T_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_int16_t_pointer = length; | |
| break; | |
| case TYPE_COUNT_INT32_T_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_int32_t_pointer = length; | |
| break; | |
| case TYPE_COUNT_INT64_T_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_int64_t_pointer = length; | |
| break; | |
| case TYPE_COUNT_INT_FAST8_T_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_int_fast8_t_pointer = length; | |
| break; | |
| case TYPE_COUNT_INT_FAST16_T_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_int_fast16_t_pointer = length; | |
| break; | |
| case TYPE_COUNT_INT_FAST32_T_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_int_fast32_t_pointer = length; | |
| break; | |
| case TYPE_COUNT_INT_FAST64_T_POINTER: | |
| *a.arg[dp->arg_index].a.a_count_int_fast64_t_pointer = length; | |
| break; | |
| default: | |
| abort (); | |
| } | |
| #else | |
| abort (); | |
| #endif | |
| } | |
| #if ENABLE_UNISTDIO | |
| /* The unistdio extensions. */ | |
| else if (dp->conversion == 'U') | |
| { | |
| arg_type type = a.arg[dp->arg_index].type; | |
| int flags = dp->flags; | |
| int has_width = 0; | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| has_width = 1; | |
| } | |
| int has_precision = 0; | |
| size_t precision = 0; | |
| if (dp->precision_start != dp->precision_end) | |
| { | |
| if (dp->precision_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->precision_arg_index].a.a_int; | |
| /* "A negative precision is taken as if the precision | |
| were omitted." */ | |
| if (arg >= 0) | |
| { | |
| precision = arg; | |
| has_precision = 1; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->precision_start + 1; | |
| precision = 0; | |
| while (digitp != dp->precision_end) | |
| precision = xsum (xtimes (precision, 10), *digitp++ - '0'); | |
| has_precision = 1; | |
| } | |
| } | |
| switch (type) | |
| { | |
| case TYPE_U8_STRING: | |
| { | |
| const uint8_t *arg = a.arg[dp->arg_index].a.a_u8_string; | |
| const uint8_t *arg_end; | |
| size_t characters; | |
| if (has_precision) | |
| { | |
| /* Use only PRECISION characters, from the left. */ | |
| arg_end = arg; | |
| characters = 0; | |
| for (; precision > 0; precision--) | |
| { | |
| int count = u8_strmblen (arg_end); | |
| if (count == 0) | |
| break; | |
| if (count < 0) | |
| goto fail_with_EILSEQ; | |
| arg_end += count; | |
| characters++; | |
| } | |
| } | |
| else if (has_width) | |
| { | |
| /* Use the entire string, and count the number of | |
| characters. */ | |
| arg_end = arg; | |
| characters = 0; | |
| for (;;) | |
| { | |
| int count = u8_strmblen (arg_end); | |
| if (count == 0) | |
| break; | |
| if (count < 0) | |
| goto fail_with_EILSEQ; | |
| arg_end += count; | |
| characters++; | |
| } | |
| } | |
| else | |
| { | |
| /* Use the entire string. */ | |
| arg_end = arg + u8_strlen (arg); | |
| /* The number of characters doesn't matter, | |
| because !has_width and therefore width==0. */ | |
| characters = 0; | |
| } | |
| if (characters < width && !(flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| # if DCHAR_IS_UINT8_T | |
| { | |
| size_t n = arg_end - arg; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_CPY (result + length, arg, n); | |
| length += n; | |
| } | |
| # else | |
| { /* Convert. */ | |
| DCHAR_T *converted = result + length; | |
| size_t converted_len = allocated - length; | |
| # if DCHAR_IS_TCHAR | |
| /* Convert from UTF-8 to locale encoding. */ | |
| converted = | |
| u8_conv_to_encoding (locale_charset (), | |
| iconveh_question_mark, | |
| arg, arg_end - arg, NULL, | |
| converted, &converted_len); | |
| # else | |
| /* Convert from UTF-8 to UTF-16/UTF-32. */ | |
| converted = | |
| U8_TO_DCHAR (arg, arg_end - arg, | |
| converted, &converted_len); | |
| # endif | |
| if (converted == NULL) | |
| goto fail_with_errno; | |
| if (converted != result + length) | |
| { | |
| ENSURE_ALLOCATION_ELSE (xsum (length, converted_len), | |
| { free (converted); goto out_of_memory; }); | |
| DCHAR_CPY (result + length, converted, converted_len); | |
| free (converted); | |
| } | |
| length += converted_len; | |
| } | |
| # endif | |
| if (characters < width && (flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| } | |
| break; | |
| case TYPE_U16_STRING: | |
| { | |
| const uint16_t *arg = a.arg[dp->arg_index].a.a_u16_string; | |
| const uint16_t *arg_end; | |
| size_t characters; | |
| if (has_precision) | |
| { | |
| /* Use only PRECISION characters, from the left. */ | |
| arg_end = arg; | |
| characters = 0; | |
| for (; precision > 0; precision--) | |
| { | |
| int count = u16_strmblen (arg_end); | |
| if (count == 0) | |
| break; | |
| if (count < 0) | |
| goto fail_with_EILSEQ; | |
| arg_end += count; | |
| characters++; | |
| } | |
| } | |
| else if (has_width) | |
| { | |
| /* Use the entire string, and count the number of | |
| characters. */ | |
| arg_end = arg; | |
| characters = 0; | |
| for (;;) | |
| { | |
| int count = u16_strmblen (arg_end); | |
| if (count == 0) | |
| break; | |
| if (count < 0) | |
| goto fail_with_EILSEQ; | |
| arg_end += count; | |
| characters++; | |
| } | |
| } | |
| else | |
| { | |
| /* Use the entire string. */ | |
| arg_end = arg + u16_strlen (arg); | |
| /* The number of characters doesn't matter, | |
| because !has_width and therefore width==0. */ | |
| characters = 0; | |
| } | |
| if (characters < width && !(flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| # if DCHAR_IS_UINT16_T | |
| { | |
| size_t n = arg_end - arg; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_CPY (result + length, arg, n); | |
| length += n; | |
| } | |
| # else | |
| { /* Convert. */ | |
| DCHAR_T *converted = result + length; | |
| size_t converted_len = allocated - length; | |
| # if DCHAR_IS_TCHAR | |
| /* Convert from UTF-16 to locale encoding. */ | |
| converted = | |
| u16_conv_to_encoding (locale_charset (), | |
| iconveh_question_mark, | |
| arg, arg_end - arg, NULL, | |
| converted, &converted_len); | |
| # else | |
| /* Convert from UTF-16 to UTF-8/UTF-32. */ | |
| converted = | |
| U16_TO_DCHAR (arg, arg_end - arg, | |
| converted, &converted_len); | |
| # endif | |
| if (converted == NULL) | |
| goto fail_with_errno; | |
| if (converted != result + length) | |
| { | |
| ENSURE_ALLOCATION_ELSE (xsum (length, converted_len), | |
| { free (converted); goto out_of_memory; }); | |
| DCHAR_CPY (result + length, converted, converted_len); | |
| free (converted); | |
| } | |
| length += converted_len; | |
| } | |
| # endif | |
| if (characters < width && (flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| } | |
| break; | |
| case TYPE_U32_STRING: | |
| { | |
| const uint32_t *arg = a.arg[dp->arg_index].a.a_u32_string; | |
| const uint32_t *arg_end; | |
| size_t characters; | |
| if (has_precision) | |
| { | |
| /* Use only PRECISION characters, from the left. */ | |
| arg_end = arg; | |
| characters = 0; | |
| for (; precision > 0; precision--) | |
| { | |
| int count = u32_strmblen (arg_end); | |
| if (count == 0) | |
| break; | |
| if (count < 0) | |
| goto fail_with_EILSEQ; | |
| arg_end += count; | |
| characters++; | |
| } | |
| } | |
| else if (has_width) | |
| { | |
| /* Use the entire string, and count the number of | |
| characters. */ | |
| arg_end = arg; | |
| characters = 0; | |
| for (;;) | |
| { | |
| int count = u32_strmblen (arg_end); | |
| if (count == 0) | |
| break; | |
| if (count < 0) | |
| goto fail_with_EILSEQ; | |
| arg_end += count; | |
| characters++; | |
| } | |
| } | |
| else | |
| { | |
| /* Use the entire string. */ | |
| arg_end = arg + u32_strlen (arg); | |
| /* The number of characters doesn't matter, | |
| because !has_width and therefore width==0. */ | |
| characters = 0; | |
| } | |
| if (characters < width && !(flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| # if DCHAR_IS_UINT32_T | |
| { | |
| size_t n = arg_end - arg; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_CPY (result + length, arg, n); | |
| length += n; | |
| } | |
| # else | |
| { /* Convert. */ | |
| DCHAR_T *converted = result + length; | |
| size_t converted_len = allocated - length; | |
| # if DCHAR_IS_TCHAR | |
| /* Convert from UTF-32 to locale encoding. */ | |
| converted = | |
| u32_conv_to_encoding (locale_charset (), | |
| iconveh_question_mark, | |
| arg, arg_end - arg, NULL, | |
| converted, &converted_len); | |
| # else | |
| /* Convert from UTF-32 to UTF-8/UTF-16. */ | |
| converted = | |
| U32_TO_DCHAR (arg, arg_end - arg, | |
| converted, &converted_len); | |
| # endif | |
| if (converted == NULL) | |
| goto fail_with_errno; | |
| if (converted != result + length) | |
| { | |
| ENSURE_ALLOCATION_ELSE (xsum (length, converted_len), | |
| { free (converted); goto out_of_memory; }); | |
| DCHAR_CPY (result + length, converted, converted_len); | |
| free (converted); | |
| } | |
| length += converted_len; | |
| } | |
| # endif | |
| if (characters < width && (flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| } | |
| break; | |
| default: | |
| abort (); | |
| } | |
| } | |
| #endif | |
| #if !WIDE_CHAR_VERSION && (PTRDIFF_MAX > INT_MAX) | |
| else if (dp->conversion == 's' | |
| && a.arg[dp->arg_index].type != TYPE_WIDE_STRING) | |
| { | |
| /* %s in vasnprintf. See the specification of fprintf. | |
| We handle it ourselves here, because the string may be longer | |
| than INT_MAX characters, whence snprintf or sprintf would | |
| fail to process it. */ | |
| int flags = dp->flags; | |
| int has_width = 0; | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| has_width = 1; | |
| } | |
| int has_precision = 0; | |
| size_t precision = 6; | |
| if (dp->precision_start != dp->precision_end) | |
| { | |
| if (dp->precision_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->precision_arg_index].a.a_int; | |
| /* "A negative precision is taken as if the precision | |
| were omitted." */ | |
| if (arg >= 0) | |
| { | |
| precision = arg; | |
| has_precision = 1; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->precision_start + 1; | |
| precision = 0; | |
| while (digitp != dp->precision_end) | |
| precision = xsum (xtimes (precision, 10), *digitp++ - '0'); | |
| has_precision = 1; | |
| } | |
| } | |
| { | |
| const char *arg = a.arg[dp->arg_index].a.a_string; | |
| size_t bytes; | |
| if (has_precision) | |
| { | |
| /* Use only at most PRECISION bytes, from the left. */ | |
| bytes = local_strnlen (arg, precision); | |
| } | |
| else | |
| { | |
| /* Use the entire string, and count the number of | |
| bytes. */ | |
| bytes = strlen (arg); | |
| } | |
| # if ENABLE_UNISTDIO && DCHAR_IS_TCHAR | |
| size_t characters; | |
| if (has_width) | |
| characters = mbsnlen (arg, bytes); | |
| else | |
| { | |
| /* The number of characters doesn't matter, | |
| because !has_width and therefore width==0. */ | |
| characters = 0; | |
| } | |
| # endif | |
| # if !DCHAR_IS_TCHAR | |
| /* This code assumes that TCHAR_T is 'char'. */ | |
| static_assert (sizeof (TCHAR_T) == 1); | |
| DCHAR_T *tmpdst; | |
| size_t tmpdst_len; | |
| /* Convert from TCHAR_T[] to DCHAR_T[]. */ | |
| tmpdst = | |
| DCHAR_CONV_FROM_ENCODING (locale_charset (), | |
| iconveh_question_mark, | |
| arg, bytes, | |
| NULL, | |
| NULL, &tmpdst_len); | |
| if (tmpdst == NULL) | |
| goto fail_with_errno; | |
| # endif | |
| size_t w; | |
| if (has_width) | |
| { | |
| # if ENABLE_UNISTDIO | |
| /* Outside POSIX, it's preferable to compare the width | |
| against the number of _characters_ of the converted | |
| value. */ | |
| # if DCHAR_IS_TCHAR | |
| w = characters; | |
| # else | |
| w = DCHAR_MBSNLEN (tmpdst, tmpdst_len); | |
| # endif | |
| # else | |
| /* The width is compared against the number of _bytes_ | |
| of the converted value, says POSIX. */ | |
| w = bytes; | |
| # endif | |
| } | |
| else | |
| /* w doesn't matter. */ | |
| w = 0; | |
| { | |
| # if DCHAR_IS_TCHAR | |
| size_t total = bytes + (w < width ? width - w : 0); | |
| ENSURE_ALLOCATION (xsum (length, total)); | |
| # else | |
| size_t total = tmpdst_len + (w < width ? width - w : 0); | |
| ENSURE_ALLOCATION_ELSE (xsum (length, total), | |
| { free (tmpdst); goto out_of_memory; }); | |
| # endif | |
| if (w < width && !(flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - w; | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| # if DCHAR_IS_TCHAR | |
| memcpy (result + length, arg, bytes); | |
| length += bytes; | |
| # else | |
| DCHAR_CPY (result + length, tmpdst, tmpdst_len); | |
| free (tmpdst); | |
| length += tmpdst_len; | |
| # endif | |
| if (w < width && (flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - w; | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| } | |
| } | |
| } | |
| #endif | |
| #if WIDE_CHAR_VERSION && ((PTRDIFF_MAX > INT_MAX) || !DCHAR_IS_TCHAR || NEED_WPRINTF_DIRECTIVE_LC) | |
| else if ((dp->conversion == 's' | |
| && a.arg[dp->arg_index].type == TYPE_WIDE_STRING) | |
| || (dp->conversion == 'c' | |
| && a.arg[dp->arg_index].type == TYPE_WIDE_CHAR)) | |
| { | |
| /* %ls or %lc in vasnwprintf. See the specification of | |
| fwprintf. */ | |
| /* It would be silly to use snprintf ("%ls", ...) and then | |
| convert back the result from a char[] to a wchar_t[]. | |
| Instead, just copy the argument wchar_t[] to the result. */ | |
| int flags = dp->flags; | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| } | |
| { | |
| const wchar_t *ls_arg; | |
| wchar_t lc_arg[1]; | |
| size_t characters; | |
| if (dp->conversion == 's') | |
| { | |
| int has_precision = 0; | |
| size_t precision = 6; | |
| if (dp->precision_start != dp->precision_end) | |
| { | |
| if (dp->precision_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->precision_arg_index].a.a_int; | |
| /* "A negative precision is taken as if the precision | |
| were omitted." */ | |
| if (arg >= 0) | |
| { | |
| precision = arg; | |
| has_precision = 1; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->precision_start + 1; | |
| precision = 0; | |
| while (digitp != dp->precision_end) | |
| precision = xsum (xtimes (precision, 10), *digitp++ - '0'); | |
| has_precision = 1; | |
| } | |
| } | |
| ls_arg = a.arg[dp->arg_index].a.a_wide_string; | |
| if (has_precision) | |
| { | |
| /* Use only at most PRECISION wide characters, from | |
| the left. */ | |
| const wchar_t *ls_arg_end = ls_arg; | |
| characters = 0; | |
| for (; precision > 0; precision--) | |
| { | |
| if (*ls_arg_end == 0) | |
| /* Found the terminating null wide character. */ | |
| break; | |
| ls_arg_end++; | |
| characters++; | |
| } | |
| } | |
| else | |
| { | |
| /* Use the entire string, and count the number of wide | |
| characters. */ | |
| characters = local_wcslen (ls_arg); | |
| } | |
| } | |
| else /* dp->conversion == 'c' */ | |
| { | |
| lc_arg[0] = (wchar_t) a.arg[dp->arg_index].a.a_wide_char; | |
| ls_arg = lc_arg; | |
| characters = 1; | |
| } | |
| { | |
| size_t total = (characters < width ? width : characters); | |
| ENSURE_ALLOCATION (xsum (length, total)); | |
| if (characters < width && !(flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| if (characters > 0) | |
| { | |
| DCHAR_CPY (result + length, ls_arg, characters); | |
| length += characters; | |
| } | |
| if (characters < width && (flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| } | |
| } | |
| } | |
| #endif | |
| #if WIDE_CHAR_VERSION || !USE_SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK | |
| else if (dp->conversion == 's' | |
| # if WIDE_CHAR_VERSION | |
| && a.arg[dp->arg_index].type != TYPE_WIDE_STRING | |
| # else | |
| && a.arg[dp->arg_index].type == TYPE_WIDE_STRING | |
| # endif | |
| ) | |
| { | |
| /* The normal handling of the 's' directive below requires | |
| allocating a temporary buffer. The determination of its | |
| length (tmp_length), in the case when a precision is | |
| specified, below requires a conversion between a char[] | |
| string and a wchar_t[] wide string. It could be done, but | |
| we have no guarantee that the implementation of sprintf will | |
| use the exactly same algorithm. Without this guarantee, it | |
| is possible to have buffer overrun bugs. In order to avoid | |
| such bugs, we implement the entire processing of the 's' | |
| directive ourselves. */ | |
| int flags = dp->flags; | |
| int has_width = 0; | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| has_width = 1; | |
| } | |
| int has_precision = 0; | |
| size_t precision = 6; | |
| if (dp->precision_start != dp->precision_end) | |
| { | |
| if (dp->precision_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->precision_arg_index].a.a_int; | |
| /* "A negative precision is taken as if the precision | |
| were omitted." */ | |
| if (arg >= 0) | |
| { | |
| precision = arg; | |
| has_precision = 1; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->precision_start + 1; | |
| precision = 0; | |
| while (digitp != dp->precision_end) | |
| precision = xsum (xtimes (precision, 10), *digitp++ - '0'); | |
| has_precision = 1; | |
| } | |
| } | |
| # if WIDE_CHAR_VERSION | |
| /* %s in vasnwprintf. See the specification of fwprintf. */ | |
| { | |
| const char *arg = a.arg[dp->arg_index].a.a_string; | |
| const char *arg_end; | |
| size_t characters; | |
| if (has_precision) | |
| { | |
| /* Use only as many bytes as needed to produce PRECISION | |
| wide characters, from the left. */ | |
| # if HAVE_MBRTOWC | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| arg_end = arg; | |
| characters = 0; | |
| for (; precision > 0; precision--) | |
| { | |
| int count; | |
| # if HAVE_MBRTOWC | |
| count = mbrlen (arg_end, MB_CUR_MAX, &state); | |
| # else | |
| count = mblen (arg_end, MB_CUR_MAX); | |
| # endif | |
| if (count == 0) | |
| /* Found the terminating NUL. */ | |
| break; | |
| if (count < 0) | |
| /* Invalid or incomplete multibyte character. */ | |
| goto fail_with_EILSEQ; | |
| arg_end += count; | |
| characters++; | |
| } | |
| } | |
| else if (has_width) | |
| { | |
| /* Use the entire string, and count the number of wide | |
| characters. */ | |
| # if HAVE_MBRTOWC | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| arg_end = arg; | |
| characters = 0; | |
| for (;;) | |
| { | |
| int count; | |
| # if HAVE_MBRTOWC | |
| count = mbrlen (arg_end, MB_CUR_MAX, &state); | |
| # else | |
| count = mblen (arg_end, MB_CUR_MAX); | |
| # endif | |
| if (count == 0) | |
| /* Found the terminating NUL. */ | |
| break; | |
| if (count < 0) | |
| /* Invalid or incomplete multibyte character. */ | |
| goto fail_with_EILSEQ; | |
| arg_end += count; | |
| characters++; | |
| } | |
| } | |
| else | |
| { | |
| /* Use the entire string. */ | |
| arg_end = arg + strlen (arg); | |
| /* The number of characters doesn't matter. */ | |
| characters = 0; | |
| } | |
| if (characters < width && !(flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| if (has_precision || has_width) | |
| { | |
| /* We know the number of wide characters in advance. */ | |
| # if HAVE_MBRTOWC | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| ENSURE_ALLOCATION (xsum (length, characters)); | |
| for (size_t remaining = characters; remaining > 0; remaining--) | |
| { | |
| wchar_t wc; | |
| int count; | |
| # if HAVE_MBRTOWC | |
| count = mbrtowc (&wc, arg, arg_end - arg, &state); | |
| # else | |
| count = mbtowc (&wc, arg, arg_end - arg); | |
| # endif | |
| if (count <= 0) | |
| /* mbrtowc not consistent with mbrlen, or mbtowc | |
| not consistent with mblen. */ | |
| abort (); | |
| result[length++] = wc; | |
| arg += count; | |
| } | |
| if (!(arg == arg_end)) | |
| abort (); | |
| } | |
| else | |
| { | |
| # if HAVE_MBRTOWC | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| while (arg < arg_end) | |
| { | |
| wchar_t wc; | |
| int count; | |
| # if HAVE_MBRTOWC | |
| count = mbrtowc (&wc, arg, arg_end - arg, &state); | |
| # else | |
| count = mbtowc (&wc, arg, arg_end - arg); | |
| # endif | |
| if (count == 0) | |
| /* mbrtowc not consistent with strlen. */ | |
| abort (); | |
| if (count < 0) | |
| /* Invalid or incomplete multibyte character. */ | |
| goto fail_with_EILSEQ; | |
| ENSURE_ALLOCATION (xsum (length, 1)); | |
| result[length++] = wc; | |
| arg += count; | |
| } | |
| } | |
| if (characters < width && (flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - characters; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| } | |
| # else | |
| /* %ls in vasnprintf. See the specification of fprintf. */ | |
| { | |
| const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string; | |
| const wchar_t *arg_end; | |
| size_t bytes; | |
| # if ENABLE_UNISTDIO && DCHAR_IS_TCHAR | |
| size_t characters; | |
| # endif | |
| # if !DCHAR_IS_TCHAR | |
| /* This code assumes that TCHAR_T is 'char'. */ | |
| static_assert (sizeof (TCHAR_T) == 1); | |
| DCHAR_T *tmpdst; | |
| size_t tmpdst_len; | |
| # endif | |
| size_t w; | |
| if (has_precision) | |
| { | |
| /* Use only as many wide characters as needed to produce | |
| at most PRECISION bytes, from the left. */ | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| arg_end = arg; | |
| bytes = 0; | |
| # if ENABLE_UNISTDIO && DCHAR_IS_TCHAR | |
| characters = 0; | |
| # endif | |
| while (precision > 0) | |
| { | |
| if (*arg_end == 0) | |
| /* Found the terminating null wide character. */ | |
| break; | |
| char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ | |
| int count = local_wcrtomb (cbuf, *arg_end, &state); | |
| if (count < 0) | |
| /* Cannot convert. */ | |
| goto fail_with_EILSEQ; | |
| if (precision < (unsigned int) count) | |
| break; | |
| arg_end++; | |
| bytes += count; | |
| # if ENABLE_UNISTDIO && DCHAR_IS_TCHAR | |
| characters += mbsnlen (cbuf, count); | |
| # endif | |
| precision -= count; | |
| } | |
| } | |
| # if DCHAR_IS_TCHAR | |
| else if (has_width) | |
| # else | |
| else | |
| # endif | |
| { | |
| /* Use the entire string, and count the number of | |
| bytes. */ | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| arg_end = arg; | |
| bytes = 0; | |
| # if ENABLE_UNISTDIO && DCHAR_IS_TCHAR | |
| characters = 0; | |
| # endif | |
| for (;;) | |
| { | |
| if (*arg_end == 0) | |
| /* Found the terminating null wide character. */ | |
| break; | |
| char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ | |
| int count = local_wcrtomb (cbuf, *arg_end, &state); | |
| if (count < 0) | |
| /* Cannot convert. */ | |
| goto fail_with_EILSEQ; | |
| arg_end++; | |
| bytes += count; | |
| # if ENABLE_UNISTDIO && DCHAR_IS_TCHAR | |
| characters += mbsnlen (cbuf, count); | |
| # endif | |
| } | |
| } | |
| # if DCHAR_IS_TCHAR | |
| else | |
| { | |
| /* Use the entire string. */ | |
| arg_end = arg + local_wcslen (arg); | |
| /* The number of bytes and characters doesn't matter, | |
| because !has_width and therefore width==0. */ | |
| bytes = 0; | |
| # if ENABLE_UNISTDIO | |
| characters = 0; | |
| # endif | |
| } | |
| # endif | |
| # if !DCHAR_IS_TCHAR | |
| { | |
| /* Convert the string into a piece of temporary memory. */ | |
| TCHAR_T *tmpsrc = (TCHAR_T *) malloc (bytes * sizeof (TCHAR_T)); | |
| if (tmpsrc == NULL) | |
| goto out_of_memory; | |
| { | |
| TCHAR_T *tmpptr = tmpsrc; | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| for (size_t remaining = bytes; remaining > 0; ) | |
| { | |
| if (*arg == 0) | |
| abort (); | |
| char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ | |
| int count = local_wcrtomb (cbuf, *arg, &state); | |
| if (count <= 0) | |
| /* Inconsistency. */ | |
| abort (); | |
| memcpy (tmpptr, cbuf, count); | |
| tmpptr += count; | |
| arg++; | |
| remaining -= count; | |
| } | |
| if (!(arg == arg_end)) | |
| abort (); | |
| } | |
| /* Convert from TCHAR_T[] to DCHAR_T[]. */ | |
| tmpdst = | |
| DCHAR_CONV_FROM_ENCODING (locale_charset (), | |
| iconveh_question_mark, | |
| tmpsrc, bytes, | |
| NULL, | |
| NULL, &tmpdst_len); | |
| if (tmpdst == NULL) | |
| { | |
| free (tmpsrc); | |
| goto fail_with_errno; | |
| } | |
| free (tmpsrc); | |
| } | |
| # endif | |
| if (has_width) | |
| { | |
| # if ENABLE_UNISTDIO | |
| /* Outside POSIX, it's preferable to compare the width | |
| against the number of _characters_ of the converted | |
| value. */ | |
| # if DCHAR_IS_TCHAR | |
| w = characters; | |
| # else | |
| w = DCHAR_MBSNLEN (tmpdst, tmpdst_len); | |
| # endif | |
| # else | |
| /* The width is compared against the number of _bytes_ | |
| of the converted value, says POSIX. */ | |
| w = bytes; | |
| # endif | |
| } | |
| else | |
| /* w doesn't matter. */ | |
| w = 0; | |
| if (w < width && !(flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - w; | |
| # if DCHAR_IS_TCHAR | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| # else | |
| ENSURE_ALLOCATION_ELSE (xsum (length, n), | |
| { free (tmpdst); goto out_of_memory; }); | |
| # endif | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| # if DCHAR_IS_TCHAR | |
| if (has_precision || has_width) | |
| { | |
| /* We know the number of bytes in advance. */ | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| ENSURE_ALLOCATION (xsum (length, bytes)); | |
| for (size_t remaining = bytes; remaining > 0; ) | |
| { | |
| if (*arg == 0) | |
| abort (); | |
| char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ | |
| int count = local_wcrtomb (cbuf, *arg, &state); | |
| if (count <= 0) | |
| /* Inconsistency. */ | |
| abort (); | |
| memcpy (result + length, cbuf, count); | |
| length += count; | |
| arg++; | |
| remaining -= count; | |
| } | |
| if (!(arg == arg_end)) | |
| abort (); | |
| } | |
| else | |
| { | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| while (arg < arg_end) | |
| { | |
| if (*arg == 0) | |
| abort (); | |
| char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ | |
| int count = local_wcrtomb (cbuf, *arg, &state); | |
| if (count <= 0) | |
| /* Cannot convert. */ | |
| goto fail_with_EILSEQ; | |
| ENSURE_ALLOCATION (xsum (length, count)); | |
| memcpy (result + length, cbuf, count); | |
| length += count; | |
| arg++; | |
| } | |
| } | |
| # else | |
| ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len), | |
| { free (tmpdst); goto out_of_memory; }); | |
| DCHAR_CPY (result + length, tmpdst, tmpdst_len); | |
| free (tmpdst); | |
| length += tmpdst_len; | |
| # endif | |
| if (w < width && (flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - w; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| } | |
| # endif | |
| } | |
| #endif | |
| #if (NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION | |
| else if (dp->conversion == 'c' | |
| && a.arg[dp->arg_index].type == TYPE_WIDE_CHAR) | |
| { | |
| /* Implement the 'lc' directive ourselves, in order to provide | |
| a correct behaviour for the null wint_t argument and/or the | |
| fallback that avoids EILSEQ. */ | |
| int flags = dp->flags; | |
| int has_width = 0; | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| has_width = 1; | |
| } | |
| /* %lc in vasnprintf. See the specification of fprintf. */ | |
| { | |
| wchar_t arg = (wchar_t) a.arg[dp->arg_index].a.a_wide_char; | |
| size_t bytes; | |
| # if ENABLE_UNISTDIO && DCHAR_IS_TCHAR | |
| size_t characters; | |
| # endif | |
| # if !DCHAR_IS_TCHAR | |
| /* This code assumes that TCHAR_T is 'char'. */ | |
| static_assert (sizeof (TCHAR_T) == 1); | |
| DCHAR_T *tmpdst; | |
| size_t tmpdst_len; | |
| # endif | |
| size_t w; | |
| # if DCHAR_IS_TCHAR | |
| if (has_width) | |
| # endif | |
| { | |
| /* Count the number of bytes. */ | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ | |
| int count = local_wcrtomb (cbuf, arg, &state); | |
| if (count < 0) | |
| /* Cannot convert. */ | |
| goto fail_with_EILSEQ; | |
| bytes = count; | |
| # if ENABLE_UNISTDIO && DCHAR_IS_TCHAR | |
| characters = mbsnlen (cbuf, count); | |
| # endif | |
| } | |
| # if DCHAR_IS_TCHAR | |
| else | |
| { | |
| /* The number of bytes and characters doesn't matter, | |
| because !has_width and therefore width==0. */ | |
| bytes = 0; | |
| # if ENABLE_UNISTDIO | |
| characters = 0; | |
| # endif | |
| } | |
| # endif | |
| # if !DCHAR_IS_TCHAR | |
| { | |
| TCHAR_T tmpsrc[64]; /* Assume MB_CUR_MAX <= 64. */ | |
| /* Convert the string into a piece of temporary memory. */ | |
| if (bytes > 0) | |
| { | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ | |
| int count = local_wcrtomb (cbuf, arg, &state); | |
| if (count <= 0) | |
| /* Inconsistency. */ | |
| abort (); | |
| memcpy (tmpsrc, cbuf, count); | |
| } | |
| /* Convert from TCHAR_T[] to DCHAR_T[]. */ | |
| tmpdst = | |
| DCHAR_CONV_FROM_ENCODING (locale_charset (), | |
| iconveh_question_mark, | |
| tmpsrc, bytes, | |
| NULL, | |
| NULL, &tmpdst_len); | |
| if (tmpdst == NULL) | |
| goto fail_with_errno; | |
| } | |
| # endif | |
| if (has_width) | |
| { | |
| # if ENABLE_UNISTDIO | |
| /* Outside POSIX, it's preferable to compare the width | |
| against the number of _characters_ of the converted | |
| value. */ | |
| # if DCHAR_IS_TCHAR | |
| w = characters; | |
| # else | |
| w = DCHAR_MBSNLEN (tmpdst, tmpdst_len); | |
| # endif | |
| # else | |
| /* The width is compared against the number of _bytes_ | |
| of the converted value, says POSIX. */ | |
| w = bytes; | |
| # endif | |
| } | |
| else | |
| /* w doesn't matter. */ | |
| w = 0; | |
| if (w < width && !(flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - w; | |
| # if DCHAR_IS_TCHAR | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| # else | |
| ENSURE_ALLOCATION_ELSE (xsum (length, n), | |
| { free (tmpdst); goto out_of_memory; }); | |
| # endif | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| # if DCHAR_IS_TCHAR | |
| if (has_width) | |
| { | |
| /* We know the number of bytes in advance. */ | |
| ENSURE_ALLOCATION (xsum (length, bytes)); | |
| if (bytes > 0) | |
| { | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| int count = local_wcrtomb (result + length, arg, &state); | |
| if (count <= 0) | |
| /* Inconsistency. */ | |
| abort (); | |
| length += count; | |
| } | |
| } | |
| else | |
| { | |
| # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t | |
| mbstate_t state; | |
| mbszero (&state); | |
| # endif | |
| char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ | |
| int count = local_wcrtomb (cbuf, arg, &state); | |
| if (count < 0) | |
| /* Cannot convert. */ | |
| goto fail_with_EILSEQ; | |
| ENSURE_ALLOCATION (xsum (length, count)); | |
| memcpy (result + length, cbuf, count); | |
| length += count; | |
| } | |
| # else | |
| ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len), | |
| { free (tmpdst); goto out_of_memory; }); | |
| DCHAR_CPY (result + length, tmpdst, tmpdst_len); | |
| free (tmpdst); | |
| length += tmpdst_len; | |
| # endif | |
| if (w < width && (flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - w; | |
| ENSURE_ALLOCATION (xsum (length, n)); | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| } | |
| } | |
| #endif | |
| #if NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION | |
| else if (dp->conversion == 'c' | |
| && a.arg[dp->arg_index].type != TYPE_WIDE_CHAR) | |
| { | |
| /* Implement the 'c' directive ourselves, in order to avoid | |
| EILSEQ in the "C" locale. */ | |
| int flags = dp->flags; | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| } | |
| /* %c in vasnwprintf. See the specification of fwprintf. */ | |
| { | |
| char arg = (char) a.arg[dp->arg_index].a.a_char; | |
| mbstate_t state; | |
| mbszero (&state); | |
| wchar_t wc; | |
| int count = mbrtowc (&wc, &arg, 1, &state); | |
| if (count < 0) | |
| /* Invalid or incomplete multibyte character. */ | |
| goto fail_with_EILSEQ; | |
| { | |
| size_t total = (1 < width ? width : 1); | |
| ENSURE_ALLOCATION (xsum (length, total)); | |
| if (1 < width && !(flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - 1; | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| result[length++] = wc; | |
| if (1 < width && (flags & FLAG_LEFT)) | |
| { | |
| size_t n = width - 1; | |
| DCHAR_SET (result + length, ' ', n); | |
| length += n; | |
| } | |
| } | |
| } | |
| } | |
| #endif | |
| #if NEED_PRINTF_DIRECTIVE_B || NEED_PRINTF_DIRECTIVE_UPPERCASE_B | |
| else if (0 | |
| # if NEED_PRINTF_DIRECTIVE_B | |
| || (dp->conversion == 'b') | |
| # endif | |
| # if NEED_PRINTF_DIRECTIVE_UPPERCASE_B | |
| || (dp->conversion == 'B') | |
| # endif | |
| ) | |
| { | |
| arg_type type = a.arg[dp->arg_index].type; | |
| int flags = dp->flags; | |
| int has_width = 0; | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| has_width = 1; | |
| } | |
| int has_precision = 0; | |
| size_t precision = 1; | |
| if (dp->precision_start != dp->precision_end) | |
| { | |
| if (dp->precision_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->precision_arg_index].a.a_int; | |
| /* "A negative precision is taken as if the precision | |
| were omitted." */ | |
| if (arg >= 0) | |
| { | |
| precision = arg; | |
| has_precision = 1; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->precision_start + 1; | |
| precision = 0; | |
| while (digitp != dp->precision_end) | |
| precision = xsum (xtimes (precision, 10), *digitp++ - '0'); | |
| has_precision = 1; | |
| } | |
| } | |
| /* Allocate a temporary buffer of sufficient size. */ | |
| size_t tmp_length; | |
| switch (type) | |
| { | |
| default: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned int) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long int) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_ULONGLONGINT: | |
| tmp_length = | |
| (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint8_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint16_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint32_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint64_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST8_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST16_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST32_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| case TYPE_UINT_FAST64_T: | |
| tmp_length = | |
| (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT) | |
| + 1; /* turn floor into ceil */ | |
| break; | |
| } | |
| if (tmp_length < precision) | |
| tmp_length = precision; | |
| /* Add 2, to account for a prefix from the alternate form. */ | |
| tmp_length = xsum (tmp_length, 2); | |
| if (tmp_length < width) | |
| tmp_length = width; | |
| DCHAR_T tmpbuf[700]; | |
| DCHAR_T *tmp; | |
| if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) | |
| tmp = tmpbuf; | |
| else | |
| { | |
| size_t tmp_memsize = xtimes (tmp_length, sizeof (DCHAR_T)); | |
| if (size_overflow_p (tmp_memsize)) | |
| /* Overflow, would lead to out of memory. */ | |
| goto out_of_memory; | |
| tmp = (DCHAR_T *) malloc (tmp_memsize); | |
| if (tmp == NULL) | |
| /* Out of memory. */ | |
| goto out_of_memory; | |
| } | |
| DCHAR_T *tmp_end = tmp + tmp_length; | |
| unsigned long long arg; | |
| switch (type) | |
| { | |
| case TYPE_UCHAR: | |
| arg = a.arg[dp->arg_index].a.a_uchar; | |
| break; | |
| case TYPE_USHORT: | |
| arg = a.arg[dp->arg_index].a.a_ushort; | |
| break; | |
| case TYPE_UINT: | |
| arg = a.arg[dp->arg_index].a.a_uint; | |
| break; | |
| case TYPE_ULONGINT: | |
| arg = a.arg[dp->arg_index].a.a_ulongint; | |
| break; | |
| case TYPE_ULONGLONGINT: | |
| arg = a.arg[dp->arg_index].a.a_ulonglongint; | |
| break; | |
| case TYPE_UINT8_T: | |
| arg = a.arg[dp->arg_index].a.a_uint8_t; | |
| break; | |
| case TYPE_UINT16_T: | |
| arg = a.arg[dp->arg_index].a.a_uint16_t; | |
| break; | |
| case TYPE_UINT32_T: | |
| arg = a.arg[dp->arg_index].a.a_uint32_t; | |
| break; | |
| case TYPE_UINT64_T: | |
| arg = a.arg[dp->arg_index].a.a_uint64_t; | |
| break; | |
| case TYPE_UINT_FAST8_T: | |
| arg = a.arg[dp->arg_index].a.a_uint_fast8_t; | |
| break; | |
| case TYPE_UINT_FAST16_T: | |
| arg = a.arg[dp->arg_index].a.a_uint_fast16_t; | |
| break; | |
| case TYPE_UINT_FAST32_T: | |
| arg = a.arg[dp->arg_index].a.a_uint_fast32_t; | |
| break; | |
| case TYPE_UINT_FAST64_T: | |
| arg = a.arg[dp->arg_index].a.a_uint_fast64_t; | |
| break; | |
| default: | |
| abort (); | |
| } | |
| int need_prefix = ((flags & FLAG_ALT) && arg != 0); | |
| DCHAR_T *p = tmp_end; | |
| /* "The result of converting a zero value with a precision | |
| of zero is no characters." */ | |
| if (!(has_precision && precision == 0 && arg == 0)) | |
| { | |
| do | |
| { | |
| *--p = '0' + (arg & 1); | |
| arg = arg >> 1; | |
| } | |
| while (arg != 0); | |
| } | |
| if (has_precision) | |
| { | |
| DCHAR_T *digits_start = tmp_end - precision; | |
| while (p > digits_start) | |
| *--p = '0'; | |
| } | |
| DCHAR_T *pad_ptr = p; | |
| if (need_prefix) | |
| { | |
| # if NEED_PRINTF_DIRECTIVE_B && !NEED_PRINTF_DIRECTIVE_UPPERCASE_B | |
| *--p = 'b'; | |
| # elif NEED_PRINTF_DIRECTIVE_UPPERCASE_B && !NEED_PRINTF_DIRECTIVE_B | |
| *--p = 'B'; | |
| # else | |
| *--p = dp->conversion; | |
| # endif | |
| *--p = '0'; | |
| } | |
| DCHAR_T *tmp_start = p; | |
| /* The generated string now extends from tmp_start to tmp_end, | |
| with the zero padding insertion point being at pad_ptr, | |
| tmp_start <= pad_ptr <= tmp_end. */ | |
| size_t count = tmp_end - tmp_start; | |
| if (count < width) | |
| { | |
| size_t pad = width - count; | |
| if (flags & FLAG_LEFT) | |
| { | |
| /* Pad with spaces on the right. */ | |
| for (p = tmp_start; p < tmp_end; p++) | |
| *(p - pad) = *p; | |
| for (p = tmp_end - pad; p < tmp_end; p++) | |
| *p = ' '; | |
| } | |
| else if ((flags & FLAG_ZERO) | |
| /* Neither ISO C nor POSIX specify that the '0' | |
| flag is ignored when a width and a precision | |
| are both present. But most implementations | |
| do so. */ | |
| && !(has_width && has_precision)) | |
| { | |
| /* Pad with zeroes. */ | |
| for (p = tmp_start; p < pad_ptr; p++) | |
| *(p - pad) = *p; | |
| for (p = pad_ptr - pad; p < pad_ptr; p++) | |
| *p = '0'; | |
| } | |
| else | |
| { | |
| /* Pad with spaces on the left. */ | |
| for (p = tmp_start - pad; p < tmp_start; p++) | |
| *p = ' '; | |
| } | |
| tmp_start = tmp_start - pad; | |
| } | |
| count = tmp_end - tmp_start; | |
| if (count > tmp_length) | |
| /* tmp_length was incorrectly calculated - fix the | |
| code above! */ | |
| abort (); | |
| /* Make room for the result. */ | |
| if (count >= allocated - length) | |
| { | |
| size_t n = xsum (length, count); | |
| ENSURE_ALLOCATION_ELSE (n, | |
| { if (tmp != tmpbuf) free (tmp); goto out_of_memory; }); | |
| } | |
| /* Append the result. */ | |
| memcpy (result + length, tmp_start, count * sizeof (DCHAR_T)); | |
| if (tmp != tmpbuf) | |
| free (tmp); | |
| length += count; | |
| } | |
| #endif | |
| #if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) | |
| else if ((dp->conversion == 'a' || dp->conversion == 'A') | |
| # if !(NEED_PRINTF_DIRECTIVE_A || (NEED_PRINTF_LONG_DOUBLE && NEED_PRINTF_DOUBLE)) | |
| && (0 | |
| # if NEED_PRINTF_DOUBLE | |
| || a.arg[dp->arg_index].type == TYPE_DOUBLE | |
| # endif | |
| # if NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) | |
| || a.arg[dp->arg_index].type == TYPE_LONGDOUBLE | |
| # endif | |
| ) | |
| # endif | |
| ) | |
| { | |
| arg_type type = a.arg[dp->arg_index].type; | |
| int flags = dp->flags; | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| } | |
| int has_precision = 0; | |
| size_t precision = 0; | |
| if (dp->precision_start != dp->precision_end) | |
| { | |
| if (dp->precision_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->precision_arg_index].a.a_int; | |
| /* "A negative precision is taken as if the precision | |
| were omitted." */ | |
| if (arg >= 0) | |
| { | |
| precision = arg; | |
| has_precision = 1; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->precision_start + 1; | |
| precision = 0; | |
| while (digitp != dp->precision_end) | |
| precision = xsum (xtimes (precision, 10), *digitp++ - '0'); | |
| has_precision = 1; | |
| } | |
| } | |
| /* Allocate a temporary buffer of sufficient size. */ | |
| size_t tmp_length; | |
| if (type == TYPE_LONGDOUBLE) | |
| tmp_length = | |
| (unsigned int) ((LDBL_DIG + 1) | |
| * 0.831 /* decimal -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| else | |
| tmp_length = | |
| (unsigned int) ((DBL_DIG + 1) | |
| * 0.831 /* decimal -> hexadecimal */ | |
| ) | |
| + 1; /* turn floor into ceil */ | |
| if (tmp_length < precision) | |
| tmp_length = precision; | |
| /* Account for sign, decimal point etc. */ | |
| tmp_length = xsum (tmp_length, 12); | |
| if (tmp_length < width) | |
| tmp_length = width; | |
| tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ | |
| DCHAR_T tmpbuf[700]; | |
| DCHAR_T *tmp; | |
| if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) | |
| tmp = tmpbuf; | |
| else | |
| { | |
| size_t tmp_memsize = xtimes (tmp_length, sizeof (DCHAR_T)); | |
| if (size_overflow_p (tmp_memsize)) | |
| /* Overflow, would lead to out of memory. */ | |
| goto out_of_memory; | |
| tmp = (DCHAR_T *) malloc (tmp_memsize); | |
| if (tmp == NULL) | |
| /* Out of memory. */ | |
| goto out_of_memory; | |
| } | |
| DCHAR_T *pad_ptr = NULL; | |
| DCHAR_T *p = tmp; | |
| if (type == TYPE_LONGDOUBLE) | |
| { | |
| # if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) | |
| long double arg = a.arg[dp->arg_index].a.a_longdouble; | |
| if (isnanl (arg)) | |
| { | |
| if (dp->conversion == 'A') | |
| { | |
| *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; | |
| } | |
| else | |
| { | |
| *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; | |
| } | |
| } | |
| else | |
| { | |
| DECL_LONG_DOUBLE_ROUNDING | |
| BEGIN_LONG_DOUBLE_ROUNDING (); | |
| int sign = 0; | |
| if (signbit (arg)) /* arg < 0.0L or negative zero */ | |
| { | |
| sign = -1; | |
| arg = -arg; | |
| } | |
| if (sign < 0) | |
| *p++ = '-'; | |
| else if (flags & FLAG_SHOWSIGN) | |
| *p++ = '+'; | |
| else if (flags & FLAG_SPACE) | |
| *p++ = ' '; | |
| if (arg > 0.0L && arg + arg == arg) | |
| { | |
| if (dp->conversion == 'A') | |
| { | |
| *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; | |
| } | |
| else | |
| { | |
| *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; | |
| } | |
| } | |
| else | |
| { | |
| int exponent; | |
| long double mantissa; | |
| if (arg > 0.0L) | |
| mantissa = printf_frexpl (arg, &exponent); | |
| else | |
| { | |
| exponent = 0; | |
| mantissa = 0.0L; | |
| } | |
| if (has_precision | |
| && precision < (unsigned int) ((LDBL_DIG + 1) * 0.831) + 1) | |
| { | |
| /* Round the mantissa. */ | |
| long double tail = mantissa; | |
| for (size_t q = precision; ; q--) | |
| { | |
| int digit = (int) tail; | |
| tail -= digit; | |
| if (q == 0) | |
| { | |
| if (digit & 1 ? tail >= 0.5L : tail > 0.5L) | |
| tail = 1 - tail; | |
| else | |
| tail = - tail; | |
| break; | |
| } | |
| tail *= 16.0L; | |
| } | |
| if (tail != 0.0L) | |
| for (size_t q = precision; q > 0; q--) | |
| tail *= 0.0625L; | |
| mantissa += tail; | |
| } | |
| *p++ = '0'; | |
| *p++ = dp->conversion - 'A' + 'X'; | |
| pad_ptr = p; | |
| { | |
| int digit = (int) mantissa; | |
| mantissa -= digit; | |
| *p++ = '0' + digit; | |
| if ((flags & FLAG_ALT) | |
| || mantissa > 0.0L || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| /* This loop terminates because we assume | |
| that FLT_RADIX is a power of 2. */ | |
| while (mantissa > 0.0L) | |
| { | |
| mantissa *= 16.0L; | |
| digit = (int) mantissa; | |
| mantissa -= digit; | |
| *p++ = digit | |
| + (digit < 10 | |
| ? '0' | |
| : dp->conversion - 10); | |
| if (precision > 0) | |
| precision--; | |
| } | |
| while (precision > 0) | |
| { | |
| *p++ = '0'; | |
| precision--; | |
| } | |
| } | |
| } | |
| *p++ = dp->conversion - 'A' + 'P'; | |
| # if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR | |
| { | |
| static const wchar_t decimal_format[] = | |
| { '%', '+', 'd', '\0' }; | |
| SNPRINTF (p, 6 + 1, decimal_format, exponent); | |
| } | |
| while (*p != '\0') | |
| p++; | |
| # else | |
| if (sizeof (DCHAR_T) == 1) | |
| { | |
| sprintf ((char *) p, "%+d", exponent); | |
| while (*p != '\0') | |
| p++; | |
| } | |
| else | |
| { | |
| char expbuf[6 + 1]; | |
| sprintf (expbuf, "%+d", exponent); | |
| for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) | |
| p++; | |
| } | |
| # endif | |
| } | |
| END_LONG_DOUBLE_ROUNDING (); | |
| } | |
| # else | |
| abort (); | |
| # endif | |
| } | |
| else | |
| { | |
| # if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE | |
| double arg = a.arg[dp->arg_index].a.a_double; | |
| if (isnand (arg)) | |
| { | |
| if (dp->conversion == 'A') | |
| { | |
| *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; | |
| } | |
| else | |
| { | |
| *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; | |
| } | |
| } | |
| else | |
| { | |
| int sign = 0; | |
| if (signbit (arg)) /* arg < 0.0 or negative zero */ | |
| { | |
| sign = -1; | |
| arg = -arg; | |
| } | |
| if (sign < 0) | |
| *p++ = '-'; | |
| else if (flags & FLAG_SHOWSIGN) | |
| *p++ = '+'; | |
| else if (flags & FLAG_SPACE) | |
| *p++ = ' '; | |
| if (arg > 0.0 && arg + arg == arg) | |
| { | |
| if (dp->conversion == 'A') | |
| { | |
| *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; | |
| } | |
| else | |
| { | |
| *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; | |
| } | |
| } | |
| else | |
| { | |
| int exponent; | |
| double mantissa; | |
| if (arg > 0.0) | |
| mantissa = printf_frexp (arg, &exponent); | |
| else | |
| { | |
| exponent = 0; | |
| mantissa = 0.0; | |
| } | |
| if (has_precision | |
| && precision < (unsigned int) ((DBL_DIG + 1) * 0.831) + 1) | |
| { | |
| /* Round the mantissa. */ | |
| double tail = mantissa; | |
| for (size_t q = precision; ; q--) | |
| { | |
| int digit = (int) tail; | |
| tail -= digit; | |
| if (q == 0) | |
| { | |
| if (digit & 1 ? tail >= 0.5 : tail > 0.5) | |
| tail = 1 - tail; | |
| else | |
| tail = - tail; | |
| break; | |
| } | |
| tail *= 16.0; | |
| } | |
| if (tail != 0.0) | |
| for (size_t q = precision; q > 0; q--) | |
| tail *= 0.0625; | |
| mantissa += tail; | |
| } | |
| *p++ = '0'; | |
| *p++ = dp->conversion - 'A' + 'X'; | |
| pad_ptr = p; | |
| { | |
| int digit = (int) mantissa; | |
| mantissa -= digit; | |
| *p++ = '0' + digit; | |
| if ((flags & FLAG_ALT) | |
| || mantissa > 0.0 || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| /* This loop terminates because we assume | |
| that FLT_RADIX is a power of 2. */ | |
| while (mantissa > 0.0) | |
| { | |
| mantissa *= 16.0; | |
| digit = (int) mantissa; | |
| mantissa -= digit; | |
| *p++ = digit | |
| + (digit < 10 | |
| ? '0' | |
| : dp->conversion - 10); | |
| if (precision > 0) | |
| precision--; | |
| } | |
| while (precision > 0) | |
| { | |
| *p++ = '0'; | |
| precision--; | |
| } | |
| } | |
| } | |
| *p++ = dp->conversion - 'A' + 'P'; | |
| # if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR | |
| { | |
| static const wchar_t decimal_format[] = | |
| { '%', '+', 'd', '\0' }; | |
| SNPRINTF (p, 6 + 1, decimal_format, exponent); | |
| } | |
| while (*p != '\0') | |
| p++; | |
| # else | |
| if (sizeof (DCHAR_T) == 1) | |
| { | |
| sprintf ((char *) p, "%+d", exponent); | |
| while (*p != '\0') | |
| p++; | |
| } | |
| else | |
| { | |
| char expbuf[6 + 1]; | |
| sprintf (expbuf, "%+d", exponent); | |
| for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) | |
| p++; | |
| } | |
| # endif | |
| } | |
| } | |
| # else | |
| abort (); | |
| # endif | |
| } | |
| /* The generated string now extends from tmp to p, with the | |
| zero padding insertion point being at pad_ptr. */ | |
| size_t count = p - tmp; | |
| if (count < width) | |
| { | |
| size_t pad = width - count; | |
| DCHAR_T *end = p + pad; | |
| if (flags & FLAG_LEFT) | |
| { | |
| /* Pad with spaces on the right. */ | |
| for (; pad > 0; pad--) | |
| *p++ = ' '; | |
| } | |
| else if ((flags & FLAG_ZERO) && pad_ptr != NULL) | |
| { | |
| /* Pad with zeroes. */ | |
| DCHAR_T *q = end; | |
| while (p > pad_ptr) | |
| *--q = *--p; | |
| for (; pad > 0; pad--) | |
| *p++ = '0'; | |
| } | |
| else | |
| { | |
| /* Pad with spaces on the left. */ | |
| DCHAR_T *q = end; | |
| while (p > tmp) | |
| *--q = *--p; | |
| for (; pad > 0; pad--) | |
| *p++ = ' '; | |
| } | |
| p = end; | |
| } | |
| count = p - tmp; | |
| if (count >= tmp_length) | |
| /* tmp_length was incorrectly calculated - fix the | |
| code above! */ | |
| abort (); | |
| /* Make room for the result. */ | |
| if (count >= allocated - length) | |
| { | |
| size_t n = xsum (length, count); | |
| ENSURE_ALLOCATION_ELSE (n, | |
| { if (tmp != tmpbuf) free (tmp); goto out_of_memory; }); | |
| } | |
| /* Append the result. */ | |
| memcpy (result + length, tmp, count * sizeof (DCHAR_T)); | |
| if (tmp != tmpbuf) | |
| free (tmp); | |
| length += count; | |
| } | |
| #endif | |
| #if NEED_PRINTF_INFINITE_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_LONG_DOUBLE | |
| else if ((dp->conversion == 'f' || dp->conversion == 'F' | |
| || dp->conversion == 'e' || dp->conversion == 'E' | |
| || dp->conversion == 'g' || dp->conversion == 'G' | |
| || dp->conversion == 'a' || dp->conversion == 'A') | |
| && (0 | |
| # if NEED_PRINTF_DOUBLE | |
| || a.arg[dp->arg_index].type == TYPE_DOUBLE | |
| # elif NEED_PRINTF_INFINITE_DOUBLE | |
| || (a.arg[dp->arg_index].type == TYPE_DOUBLE | |
| /* The systems (mingw) which produce wrong output | |
| for Inf, -Inf, and NaN also do so for -0.0. | |
| Therefore we treat this case here as well. */ | |
| && is_infinite_or_zero (a.arg[dp->arg_index].a.a_double)) | |
| # endif | |
| # if NEED_PRINTF_LONG_DOUBLE | |
| || a.arg[dp->arg_index].type == TYPE_LONGDOUBLE | |
| # elif NEED_PRINTF_INFINITE_LONG_DOUBLE | |
| || (a.arg[dp->arg_index].type == TYPE_LONGDOUBLE | |
| /* Some systems produce wrong output for Inf, | |
| -Inf, and NaN. Some systems in this category | |
| also do so for -0.0. Therefore we treat this | |
| case here as well. */ | |
| && is_infinite_or_zerol (a.arg[dp->arg_index].a.a_longdouble)) | |
| # endif | |
| )) | |
| { | |
| # if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE) | |
| arg_type type = a.arg[dp->arg_index].type; | |
| # endif | |
| int flags = dp->flags; | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| } | |
| int has_precision = 0; | |
| size_t precision = 0; | |
| if (dp->precision_start != dp->precision_end) | |
| { | |
| if (dp->precision_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->precision_arg_index].a.a_int; | |
| /* "A negative precision is taken as if the precision | |
| were omitted." */ | |
| if (arg >= 0) | |
| { | |
| precision = arg; | |
| has_precision = 1; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->precision_start + 1; | |
| precision = 0; | |
| while (digitp != dp->precision_end) | |
| precision = xsum (xtimes (precision, 10), *digitp++ - '0'); | |
| has_precision = 1; | |
| } | |
| } | |
| /* POSIX specifies the default precision to be 6 for %f, %F, | |
| %e, %E, but not for %g, %G. Implementations appear to use | |
| the same default precision also for %g, %G. But for %a, %A, | |
| the default precision is 0. */ | |
| if (!has_precision) | |
| if (!(dp->conversion == 'a' || dp->conversion == 'A')) | |
| precision = 6; | |
| /* Allocate a temporary buffer of sufficient size. */ | |
| size_t tmp_length; | |
| # if NEED_PRINTF_DOUBLE && NEED_PRINTF_LONG_DOUBLE | |
| tmp_length = (type == TYPE_LONGDOUBLE ? LDBL_DIG + 1 : DBL_DIG + 1); | |
| # elif NEED_PRINTF_INFINITE_DOUBLE && NEED_PRINTF_LONG_DOUBLE | |
| tmp_length = (type == TYPE_LONGDOUBLE ? LDBL_DIG + 1 : 0); | |
| # elif NEED_PRINTF_LONG_DOUBLE | |
| tmp_length = LDBL_DIG + 1; | |
| # elif NEED_PRINTF_DOUBLE | |
| tmp_length = DBL_DIG + 1; | |
| # else | |
| tmp_length = 0; | |
| # endif | |
| if (tmp_length < precision) | |
| tmp_length = precision; | |
| # if NEED_PRINTF_LONG_DOUBLE | |
| # if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE | |
| if (type == TYPE_LONGDOUBLE) | |
| # endif | |
| if (dp->conversion == 'f' || dp->conversion == 'F') | |
| { | |
| long double arg = a.arg[dp->arg_index].a.a_longdouble; | |
| if (!(isnanl (arg) || arg + arg == arg)) | |
| { | |
| /* arg is finite and nonzero. */ | |
| int exponent = floorlog10l (arg < 0 ? -arg : arg); | |
| if (exponent >= 0 && tmp_length < exponent + precision) | |
| tmp_length = exponent + precision; | |
| } | |
| } | |
| # endif | |
| # if NEED_PRINTF_DOUBLE | |
| # if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE | |
| if (type == TYPE_DOUBLE) | |
| # endif | |
| if (dp->conversion == 'f' || dp->conversion == 'F') | |
| { | |
| double arg = a.arg[dp->arg_index].a.a_double; | |
| if (!(isnand (arg) || arg + arg == arg)) | |
| { | |
| /* arg is finite and nonzero. */ | |
| int exponent = floorlog10 (arg < 0 ? -arg : arg); | |
| if (exponent >= 0 && tmp_length < exponent + precision) | |
| tmp_length = exponent + precision; | |
| } | |
| } | |
| # endif | |
| /* Account for thousands separators. */ | |
| if (flags & FLAG_GROUP) | |
| { | |
| /* A thousands separator needs to be inserted at most every 2 digits. | |
| This is the case in the ta_IN locale. */ | |
| # if WIDE_CHAR_VERSION | |
| tmp_length = xsum (tmp_length, tmp_length / 2 * THOUSEP_WCHAR_MAXLEN); | |
| # else | |
| tmp_length = xsum (tmp_length, tmp_length / 2 * THOUSEP_CHAR_MAXLEN); | |
| # endif | |
| } | |
| /* Account for sign, decimal point etc. */ | |
| tmp_length = xsum (tmp_length, 12); | |
| if (tmp_length < width) | |
| tmp_length = width; | |
| tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ | |
| DCHAR_T tmpbuf[700]; | |
| DCHAR_T *tmp; | |
| if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) | |
| tmp = tmpbuf; | |
| else | |
| { | |
| size_t tmp_memsize = xtimes (tmp_length, sizeof (DCHAR_T)); | |
| if (size_overflow_p (tmp_memsize)) | |
| /* Overflow, would lead to out of memory. */ | |
| goto out_of_memory; | |
| tmp = (DCHAR_T *) malloc (tmp_memsize); | |
| if (tmp == NULL) | |
| /* Out of memory. */ | |
| goto out_of_memory; | |
| } | |
| DCHAR_T *pad_ptr = NULL; | |
| DCHAR_T *p = tmp; | |
| # if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE | |
| # if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE | |
| if (type == TYPE_LONGDOUBLE) | |
| # endif | |
| { | |
| long double arg = a.arg[dp->arg_index].a.a_longdouble; | |
| if (isnanl (arg)) | |
| { | |
| if (dp->conversion >= 'A' && dp->conversion <= 'Z') | |
| { | |
| *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; | |
| } | |
| else | |
| { | |
| *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; | |
| } | |
| } | |
| else | |
| { | |
| DECL_LONG_DOUBLE_ROUNDING | |
| BEGIN_LONG_DOUBLE_ROUNDING (); | |
| int sign = 0; | |
| if (signbit (arg)) /* arg < 0.0L or negative zero */ | |
| { | |
| sign = -1; | |
| arg = -arg; | |
| } | |
| if (sign < 0) | |
| *p++ = '-'; | |
| else if (flags & FLAG_SHOWSIGN) | |
| *p++ = '+'; | |
| else if (flags & FLAG_SPACE) | |
| *p++ = ' '; | |
| if (arg > 0.0L && arg + arg == arg) | |
| { | |
| if (dp->conversion >= 'A' && dp->conversion <= 'Z') | |
| { | |
| *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; | |
| } | |
| else | |
| { | |
| *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; | |
| } | |
| } | |
| else | |
| { | |
| # if NEED_PRINTF_LONG_DOUBLE | |
| pad_ptr = p; | |
| if (dp->conversion == 'f' || dp->conversion == 'F') | |
| { | |
| char *digits = | |
| scale10_round_decimal_long_double (arg, precision); | |
| if (digits == NULL) | |
| { | |
| END_LONG_DOUBLE_ROUNDING (); | |
| goto out_of_memory; | |
| } | |
| size_t ndigits = strlen (digits); | |
| if (ndigits > precision) | |
| { | |
| /* Number of digits before the decimal point. */ | |
| size_t intpart_digits = ndigits - precision; | |
| const DCHAR_T *thousep = NULL; | |
| DCHAR_T thousep_buf[10]; | |
| # if !WIDE_CHAR_VERSION | |
| size_t thousep_len = 0; | |
| # endif | |
| const signed char *grouping; | |
| size_t insert = 0; | |
| if ((flags & FLAG_GROUP) && (intpart_digits > 1)) | |
| { | |
| /* Determine the thousands separator and | |
| the grouping rule of the current locale. */ | |
| # if WIDE_CHAR_VERSION | |
| /* DCHAR_T is wchar_t. */ | |
| thousep = thousands_separator_wchar (thousep_buf); | |
| # define thousep_len 1 | |
| # elif defined DCHAR_CONV_FROM_ENCODING | |
| /* DCHAR_T is uintN_t. */ | |
| thousep = thousands_separator_DCHAR (thousep_buf); | |
| thousep_len = DCHAR_STRLEN (thousep); | |
| # else | |
| /* DCHAR_T is char. */ | |
| thousep = thousands_separator_char (thousep_buf); | |
| thousep_len = strlen (thousep); | |
| # endif | |
| if (*thousep == 0) | |
| thousep = NULL; | |
| if (thousep != NULL) | |
| { | |
| grouping = grouping_rule (); | |
| insert = | |
| num_thousands_separators (grouping, intpart_digits); | |
| } | |
| } | |
| const char *digitp = digits + precision; | |
| DCHAR_T *p_before_intpart = p; | |
| p += intpart_digits + insert * thousep_len; | |
| DCHAR_T *p_after_intpart = p; | |
| if (insert > 0) /* implies (flag & FLAG_GROUP) && (thousep != NULL) */ | |
| { | |
| const signed char *g = grouping; | |
| for (;;) | |
| { | |
| int h = *g; | |
| if (h <= 0) | |
| abort (); | |
| int i = h; | |
| do | |
| *--p = *digitp++; | |
| while (--i > 0); | |
| # if WIDE_CHAR_VERSION | |
| *--p = thousep[0]; | |
| # else | |
| p -= thousep_len; | |
| DCHAR_CPY (p, thousep, thousep_len); | |
| # endif | |
| insert--; | |
| if (insert == 0) | |
| break; | |
| if (g[1] != 0) | |
| g++; | |
| } | |
| } | |
| for (;;) | |
| { | |
| *--p = *digitp++; | |
| if (p == p_before_intpart) | |
| break; | |
| } | |
| p = p_after_intpart; | |
| ndigits = precision; | |
| # undef thousep_len | |
| } | |
| else | |
| *p++ = '0'; | |
| /* Here ndigits <= precision. */ | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| for (; precision > ndigits; precision--) | |
| *p++ = '0'; | |
| while (ndigits > 0) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| free (digits); | |
| } | |
| else if (dp->conversion == 'e' || dp->conversion == 'E') | |
| { | |
| int exponent; | |
| if (arg == 0.0L) | |
| { | |
| exponent = 0; | |
| *p++ = '0'; | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| for (; precision > 0; precision--) | |
| *p++ = '0'; | |
| } | |
| } | |
| else | |
| { | |
| /* arg > 0.0L. */ | |
| exponent = floorlog10l (arg); | |
| int adjusted = 0; | |
| char *digits; | |
| size_t ndigits; | |
| for (;;) | |
| { | |
| digits = | |
| scale10_round_decimal_long_double (arg, | |
| (int)precision - exponent); | |
| if (digits == NULL) | |
| { | |
| END_LONG_DOUBLE_ROUNDING (); | |
| goto out_of_memory; | |
| } | |
| ndigits = strlen (digits); | |
| if (ndigits == precision + 1) | |
| break; | |
| if (ndigits < precision | |
| || ndigits > precision + 2) | |
| /* The exponent was not guessed | |
| precisely enough. */ | |
| abort (); | |
| if (adjusted) | |
| /* None of two values of exponent is | |
| the right one. Prevent an endless | |
| loop. */ | |
| abort (); | |
| free (digits); | |
| if (ndigits == precision) | |
| exponent -= 1; | |
| else | |
| exponent += 1; | |
| adjusted = 1; | |
| } | |
| /* Here ndigits = precision+1. */ | |
| if (is_borderline (digits, precision)) | |
| { | |
| /* Maybe the exponent guess was too high | |
| and a smaller exponent can be reached | |
| by turning a 10...0 into 9...9x. */ | |
| char *digits2 = | |
| scale10_round_decimal_long_double (arg, | |
| (int)precision - exponent + 1); | |
| if (digits2 == NULL) | |
| { | |
| free (digits); | |
| END_LONG_DOUBLE_ROUNDING (); | |
| goto out_of_memory; | |
| } | |
| if (strlen (digits2) == precision + 1) | |
| { | |
| free (digits); | |
| digits = digits2; | |
| exponent -= 1; | |
| } | |
| else | |
| free (digits2); | |
| } | |
| /* Here ndigits = precision+1. */ | |
| *p++ = digits[--ndigits]; | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| while (ndigits > 0) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| free (digits); | |
| } | |
| *p++ = dp->conversion; /* 'e' or 'E' */ | |
| # if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR | |
| { | |
| static const wchar_t decimal_format[] = | |
| { '%', '+', '.', '2', 'd', '\0' }; | |
| SNPRINTF (p, 6 + 1, decimal_format, exponent); | |
| } | |
| while (*p != '\0') | |
| p++; | |
| # else | |
| if (sizeof (DCHAR_T) == 1) | |
| { | |
| sprintf ((char *) p, "%+.2d", exponent); | |
| while (*p != '\0') | |
| p++; | |
| } | |
| else | |
| { | |
| char expbuf[6 + 1]; | |
| sprintf (expbuf, "%+.2d", exponent); | |
| for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) | |
| p++; | |
| } | |
| # endif | |
| } | |
| else if (dp->conversion == 'g' || dp->conversion == 'G') | |
| { | |
| if (precision == 0) | |
| precision = 1; | |
| /* precision >= 1. */ | |
| if (arg == 0.0L) | |
| /* The exponent is 0, >= -4, < precision. | |
| Use fixed-point notation. */ | |
| { | |
| size_t ndigits = precision; | |
| /* Number of trailing zeroes that have to be | |
| dropped. */ | |
| size_t nzeroes = | |
| (flags & FLAG_ALT ? 0 : precision - 1); | |
| --ndigits; | |
| *p++ = '0'; | |
| if ((flags & FLAG_ALT) || ndigits > nzeroes) | |
| { | |
| *p++ = decimal_point_char (); | |
| while (ndigits > nzeroes) | |
| { | |
| --ndigits; | |
| *p++ = '0'; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| /* arg > 0.0L. */ | |
| int exponent = floorlog10l (arg); | |
| int adjusted = 0; | |
| char *digits; | |
| size_t ndigits; | |
| for (;;) | |
| { | |
| digits = | |
| scale10_round_decimal_long_double (arg, | |
| (int)(precision - 1) - exponent); | |
| if (digits == NULL) | |
| { | |
| END_LONG_DOUBLE_ROUNDING (); | |
| goto out_of_memory; | |
| } | |
| ndigits = strlen (digits); | |
| if (ndigits == precision) | |
| break; | |
| if (ndigits < precision - 1 | |
| || ndigits > precision + 1) | |
| /* The exponent was not guessed | |
| precisely enough. */ | |
| abort (); | |
| if (adjusted) | |
| /* None of two values of exponent is | |
| the right one. Prevent an endless | |
| loop. */ | |
| abort (); | |
| free (digits); | |
| if (ndigits < precision) | |
| exponent -= 1; | |
| else | |
| exponent += 1; | |
| adjusted = 1; | |
| } | |
| /* Here ndigits = precision. */ | |
| if (is_borderline (digits, precision - 1)) | |
| { | |
| /* Maybe the exponent guess was too high | |
| and a smaller exponent can be reached | |
| by turning a 10...0 into 9...9x. */ | |
| char *digits2 = | |
| scale10_round_decimal_long_double (arg, | |
| (int)(precision - 1) - exponent + 1); | |
| if (digits2 == NULL) | |
| { | |
| free (digits); | |
| END_LONG_DOUBLE_ROUNDING (); | |
| goto out_of_memory; | |
| } | |
| if (strlen (digits2) == precision) | |
| { | |
| free (digits); | |
| digits = digits2; | |
| exponent -= 1; | |
| } | |
| else | |
| free (digits2); | |
| } | |
| /* Here ndigits = precision. */ | |
| /* Determine the number of trailing zeroes | |
| that have to be dropped. */ | |
| size_t nzeroes = 0; | |
| if ((flags & FLAG_ALT) == 0) | |
| while (nzeroes < ndigits | |
| && digits[nzeroes] == '0') | |
| nzeroes++; | |
| /* The exponent is now determined. */ | |
| if (exponent >= -4 | |
| && exponent < (long)precision) | |
| { | |
| /* Fixed-point notation: | |
| max(exponent,0)+1 digits, then the | |
| decimal point, then the remaining | |
| digits without trailing zeroes. */ | |
| if (exponent >= 0) | |
| { | |
| /* Number of digits before the decimal point. */ | |
| size_t intpart_digits = exponent + 1; | |
| /* Note: intpart_digits <= precision = ndigits. */ | |
| const DCHAR_T *thousep = NULL; | |
| DCHAR_T thousep_buf[10]; | |
| # if !WIDE_CHAR_VERSION | |
| size_t thousep_len = 0; | |
| # endif | |
| const signed char *grouping; | |
| size_t insert = 0; | |
| if ((flags & FLAG_GROUP) && (intpart_digits > 1)) | |
| { | |
| /* Determine the thousands separator and | |
| the grouping rule of the current locale. */ | |
| # if WIDE_CHAR_VERSION | |
| /* DCHAR_T is wchar_t. */ | |
| thousep = thousands_separator_wchar (thousep_buf); | |
| # define thousep_len 1 | |
| # elif defined DCHAR_CONV_FROM_ENCODING | |
| /* DCHAR_T is uintN_t. */ | |
| thousep = thousands_separator_DCHAR (thousep_buf); | |
| thousep_len = DCHAR_STRLEN (thousep); | |
| # else | |
| /* DCHAR_T is char. */ | |
| thousep = thousands_separator_char (thousep_buf); | |
| thousep_len = strlen (thousep); | |
| # endif | |
| if (*thousep == 0) | |
| thousep = NULL; | |
| if (thousep != NULL) | |
| { | |
| grouping = grouping_rule (); | |
| insert = | |
| num_thousands_separators (grouping, intpart_digits); | |
| } | |
| } | |
| const char *digitp = digits + ndigits - intpart_digits; | |
| DCHAR_T *p_before_intpart = p; | |
| p += intpart_digits + insert * thousep_len; | |
| DCHAR_T *p_after_intpart = p; | |
| if (insert > 0) /* implies (flag & FLAG_GROUP) && (thousep != NULL) */ | |
| { | |
| const signed char *g = grouping; | |
| for (;;) | |
| { | |
| int h = *g; | |
| if (h <= 0) | |
| abort (); | |
| int i = h; | |
| do | |
| *--p = *digitp++; | |
| while (--i > 0); | |
| # if WIDE_CHAR_VERSION | |
| *--p = thousep[0]; | |
| # else | |
| p -= thousep_len; | |
| DCHAR_CPY (p, thousep, thousep_len); | |
| # endif | |
| insert--; | |
| if (insert == 0) | |
| break; | |
| if (g[1] != 0) | |
| g++; | |
| } | |
| } | |
| for (;;) | |
| { | |
| *--p = *digitp++; | |
| if (p == p_before_intpart) | |
| break; | |
| } | |
| p = p_after_intpart; | |
| ndigits -= intpart_digits; | |
| # undef thousep_len | |
| if ((flags & FLAG_ALT) || ndigits > nzeroes) | |
| { | |
| *p++ = decimal_point_char (); | |
| while (ndigits > nzeroes) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| size_t ecount = -exponent - 1; | |
| *p++ = '0'; | |
| *p++ = decimal_point_char (); | |
| for (; ecount > 0; ecount--) | |
| *p++ = '0'; | |
| while (ndigits > nzeroes) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| /* Exponential notation. */ | |
| *p++ = digits[--ndigits]; | |
| if ((flags & FLAG_ALT) || ndigits > nzeroes) | |
| { | |
| *p++ = decimal_point_char (); | |
| while (ndigits > nzeroes) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */ | |
| # if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR | |
| { | |
| static const wchar_t decimal_format[] = | |
| { '%', '+', '.', '2', 'd', '\0' }; | |
| SNPRINTF (p, 6 + 1, decimal_format, exponent); | |
| } | |
| while (*p != '\0') | |
| p++; | |
| # else | |
| if (sizeof (DCHAR_T) == 1) | |
| { | |
| sprintf ((char *) p, "%+.2d", exponent); | |
| while (*p != '\0') | |
| p++; | |
| } | |
| else | |
| { | |
| char expbuf[6 + 1]; | |
| sprintf (expbuf, "%+.2d", exponent); | |
| for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) | |
| p++; | |
| } | |
| # endif | |
| } | |
| free (digits); | |
| } | |
| } | |
| else | |
| abort (); | |
| # else | |
| /* arg is finite. */ | |
| if (!(arg == 0.0L)) | |
| abort (); | |
| pad_ptr = p; | |
| if (dp->conversion == 'f' || dp->conversion == 'F') | |
| { | |
| *p++ = '0'; | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| for (; precision > 0; precision--) | |
| *p++ = '0'; | |
| } | |
| } | |
| else if (dp->conversion == 'e' || dp->conversion == 'E') | |
| { | |
| *p++ = '0'; | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| for (; precision > 0; precision--) | |
| *p++ = '0'; | |
| } | |
| *p++ = dp->conversion; /* 'e' or 'E' */ | |
| *p++ = '+'; | |
| *p++ = '0'; | |
| *p++ = '0'; | |
| } | |
| else if (dp->conversion == 'g' || dp->conversion == 'G') | |
| { | |
| *p++ = '0'; | |
| if (flags & FLAG_ALT) | |
| { | |
| size_t ndigits = | |
| (precision > 0 ? precision - 1 : 0); | |
| *p++ = decimal_point_char (); | |
| for (; ndigits > 0; --ndigits) | |
| *p++ = '0'; | |
| } | |
| } | |
| else if (dp->conversion == 'a' || dp->conversion == 'A') | |
| { | |
| *p++ = '0'; | |
| *p++ = dp->conversion - 'A' + 'X'; | |
| pad_ptr = p; | |
| *p++ = '0'; | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| for (; precision > 0; precision--) | |
| *p++ = '0'; | |
| } | |
| *p++ = dp->conversion - 'A' + 'P'; | |
| *p++ = '+'; | |
| *p++ = '0'; | |
| } | |
| else | |
| abort (); | |
| # endif | |
| } | |
| END_LONG_DOUBLE_ROUNDING (); | |
| } | |
| } | |
| # if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE | |
| else | |
| # endif | |
| # endif | |
| # if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE | |
| { | |
| double arg = a.arg[dp->arg_index].a.a_double; | |
| if (isnand (arg)) | |
| { | |
| if (dp->conversion >= 'A' && dp->conversion <= 'Z') | |
| { | |
| *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; | |
| } | |
| else | |
| { | |
| *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; | |
| } | |
| } | |
| else | |
| { | |
| int sign = 0; | |
| if (signbit (arg)) /* arg < 0.0 or negative zero */ | |
| { | |
| sign = -1; | |
| arg = -arg; | |
| } | |
| if (sign < 0) | |
| *p++ = '-'; | |
| else if (flags & FLAG_SHOWSIGN) | |
| *p++ = '+'; | |
| else if (flags & FLAG_SPACE) | |
| *p++ = ' '; | |
| if (arg > 0.0 && arg + arg == arg) | |
| { | |
| if (dp->conversion >= 'A' && dp->conversion <= 'Z') | |
| { | |
| *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; | |
| } | |
| else | |
| { | |
| *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; | |
| } | |
| } | |
| else | |
| { | |
| # if NEED_PRINTF_DOUBLE | |
| pad_ptr = p; | |
| if (dp->conversion == 'f' || dp->conversion == 'F') | |
| { | |
| char *digits = | |
| scale10_round_decimal_double (arg, precision); | |
| if (digits == NULL) | |
| goto out_of_memory; | |
| size_t ndigits = strlen (digits); | |
| if (ndigits > precision) | |
| { | |
| /* Number of digits before the decimal point. */ | |
| size_t intpart_digits = ndigits - precision; | |
| const DCHAR_T *thousep = NULL; | |
| DCHAR_T thousep_buf[10]; | |
| # if !WIDE_CHAR_VERSION | |
| size_t thousep_len = 0; | |
| # endif | |
| const signed char *grouping; | |
| size_t insert = 0; | |
| if ((flags & FLAG_GROUP) && (intpart_digits > 1)) | |
| { | |
| /* Determine the thousands separator and | |
| the grouping rule of the current locale. */ | |
| # if WIDE_CHAR_VERSION | |
| /* DCHAR_T is wchar_t. */ | |
| thousep = thousands_separator_wchar (thousep_buf); | |
| # define thousep_len 1 | |
| # elif defined DCHAR_CONV_FROM_ENCODING | |
| /* DCHAR_T is uintN_t. */ | |
| thousep = thousands_separator_DCHAR (thousep_buf); | |
| thousep_len = DCHAR_STRLEN (thousep); | |
| # else | |
| /* DCHAR_T is char. */ | |
| thousep = thousands_separator_char (thousep_buf); | |
| thousep_len = strlen (thousep); | |
| # endif | |
| if (*thousep == 0) | |
| thousep = NULL; | |
| if (thousep != NULL) | |
| { | |
| grouping = grouping_rule (); | |
| insert = | |
| num_thousands_separators (grouping, intpart_digits); | |
| } | |
| } | |
| const char *digitp = digits + precision; | |
| DCHAR_T *p_before_intpart = p; | |
| p += intpart_digits + insert * thousep_len; | |
| DCHAR_T *p_after_intpart = p; | |
| if (insert > 0) /* implies (flag & FLAG_GROUP) && (thousep != NULL) */ | |
| { | |
| const signed char *g = grouping; | |
| for (;;) | |
| { | |
| int h = *g; | |
| if (h <= 0) | |
| abort (); | |
| int i = h; | |
| do | |
| *--p = *digitp++; | |
| while (--i > 0); | |
| # if WIDE_CHAR_VERSION | |
| *--p = thousep[0]; | |
| # else | |
| p -= thousep_len; | |
| DCHAR_CPY (p, thousep, thousep_len); | |
| # endif | |
| insert--; | |
| if (insert == 0) | |
| break; | |
| if (g[1] != 0) | |
| g++; | |
| } | |
| } | |
| for (;;) | |
| { | |
| *--p = *digitp++; | |
| if (p == p_before_intpart) | |
| break; | |
| } | |
| p = p_after_intpart; | |
| ndigits = precision; | |
| # undef thousep_len | |
| } | |
| else | |
| *p++ = '0'; | |
| /* Here ndigits <= precision. */ | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| for (; precision > ndigits; precision--) | |
| *p++ = '0'; | |
| while (ndigits > 0) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| free (digits); | |
| } | |
| else if (dp->conversion == 'e' || dp->conversion == 'E') | |
| { | |
| int exponent; | |
| if (arg == 0.0) | |
| { | |
| exponent = 0; | |
| *p++ = '0'; | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| for (; precision > 0; precision--) | |
| *p++ = '0'; | |
| } | |
| } | |
| else | |
| { | |
| /* arg > 0.0. */ | |
| exponent = floorlog10 (arg); | |
| int adjusted = 0; | |
| char *digits; | |
| size_t ndigits; | |
| for (;;) | |
| { | |
| digits = | |
| scale10_round_decimal_double (arg, | |
| (int)precision - exponent); | |
| if (digits == NULL) | |
| goto out_of_memory; | |
| ndigits = strlen (digits); | |
| if (ndigits == precision + 1) | |
| break; | |
| if (ndigits < precision | |
| || ndigits > precision + 2) | |
| /* The exponent was not guessed | |
| precisely enough. */ | |
| abort (); | |
| if (adjusted) | |
| /* None of two values of exponent is | |
| the right one. Prevent an endless | |
| loop. */ | |
| abort (); | |
| free (digits); | |
| if (ndigits == precision) | |
| exponent -= 1; | |
| else | |
| exponent += 1; | |
| adjusted = 1; | |
| } | |
| /* Here ndigits = precision+1. */ | |
| if (is_borderline (digits, precision)) | |
| { | |
| /* Maybe the exponent guess was too high | |
| and a smaller exponent can be reached | |
| by turning a 10...0 into 9...9x. */ | |
| char *digits2 = | |
| scale10_round_decimal_double (arg, | |
| (int)precision - exponent + 1); | |
| if (digits2 == NULL) | |
| { | |
| free (digits); | |
| goto out_of_memory; | |
| } | |
| if (strlen (digits2) == precision + 1) | |
| { | |
| free (digits); | |
| digits = digits2; | |
| exponent -= 1; | |
| } | |
| else | |
| free (digits2); | |
| } | |
| /* Here ndigits = precision+1. */ | |
| *p++ = digits[--ndigits]; | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| while (ndigits > 0) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| free (digits); | |
| } | |
| *p++ = dp->conversion; /* 'e' or 'E' */ | |
| # if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR | |
| { | |
| static const wchar_t decimal_format[] = | |
| /* Produce the same number of exponent digits | |
| as the native printf implementation. */ | |
| # if defined _WIN32 && ! defined __CYGWIN__ | |
| { '%', '+', '.', '3', 'd', '\0' }; | |
| # else | |
| { '%', '+', '.', '2', 'd', '\0' }; | |
| # endif | |
| SNPRINTF (p, 6 + 1, decimal_format, exponent); | |
| } | |
| while (*p != '\0') | |
| p++; | |
| # else | |
| { | |
| static const char decimal_format[] = | |
| /* Produce the same number of exponent digits | |
| as the native printf implementation. */ | |
| # if defined _WIN32 && ! defined __CYGWIN__ | |
| "%+.3d"; | |
| # else | |
| "%+.2d"; | |
| # endif | |
| if (sizeof (DCHAR_T) == 1) | |
| { | |
| sprintf ((char *) p, decimal_format, exponent); | |
| while (*p != '\0') | |
| p++; | |
| } | |
| else | |
| { | |
| char expbuf[6 + 1]; | |
| sprintf (expbuf, decimal_format, exponent); | |
| for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) | |
| p++; | |
| } | |
| } | |
| # endif | |
| } | |
| else if (dp->conversion == 'g' || dp->conversion == 'G') | |
| { | |
| if (precision == 0) | |
| precision = 1; | |
| /* precision >= 1. */ | |
| if (arg == 0.0) | |
| /* The exponent is 0, >= -4, < precision. | |
| Use fixed-point notation. */ | |
| { | |
| size_t ndigits = precision; | |
| /* Number of trailing zeroes that have to be | |
| dropped. */ | |
| size_t nzeroes = | |
| (flags & FLAG_ALT ? 0 : precision - 1); | |
| --ndigits; | |
| *p++ = '0'; | |
| if ((flags & FLAG_ALT) || ndigits > nzeroes) | |
| { | |
| *p++ = decimal_point_char (); | |
| while (ndigits > nzeroes) | |
| { | |
| --ndigits; | |
| *p++ = '0'; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| /* arg > 0.0. */ | |
| int exponent = floorlog10 (arg); | |
| int adjusted = 0; | |
| char *digits; | |
| size_t ndigits; | |
| for (;;) | |
| { | |
| digits = | |
| scale10_round_decimal_double (arg, | |
| (int)(precision - 1) - exponent); | |
| if (digits == NULL) | |
| goto out_of_memory; | |
| ndigits = strlen (digits); | |
| if (ndigits == precision) | |
| break; | |
| if (ndigits < precision - 1 | |
| || ndigits > precision + 1) | |
| /* The exponent was not guessed | |
| precisely enough. */ | |
| abort (); | |
| if (adjusted) | |
| /* None of two values of exponent is | |
| the right one. Prevent an endless | |
| loop. */ | |
| abort (); | |
| free (digits); | |
| if (ndigits < precision) | |
| exponent -= 1; | |
| else | |
| exponent += 1; | |
| adjusted = 1; | |
| } | |
| /* Here ndigits = precision. */ | |
| if (is_borderline (digits, precision - 1)) | |
| { | |
| /* Maybe the exponent guess was too high | |
| and a smaller exponent can be reached | |
| by turning a 10...0 into 9...9x. */ | |
| char *digits2 = | |
| scale10_round_decimal_double (arg, | |
| (int)(precision - 1) - exponent + 1); | |
| if (digits2 == NULL) | |
| { | |
| free (digits); | |
| goto out_of_memory; | |
| } | |
| if (strlen (digits2) == precision) | |
| { | |
| free (digits); | |
| digits = digits2; | |
| exponent -= 1; | |
| } | |
| else | |
| free (digits2); | |
| } | |
| /* Here ndigits = precision. */ | |
| /* Determine the number of trailing zeroes | |
| that have to be dropped. */ | |
| size_t nzeroes = 0; | |
| if ((flags & FLAG_ALT) == 0) | |
| while (nzeroes < ndigits | |
| && digits[nzeroes] == '0') | |
| nzeroes++; | |
| /* The exponent is now determined. */ | |
| if (exponent >= -4 | |
| && exponent < (long)precision) | |
| { | |
| /* Fixed-point notation: | |
| max(exponent,0)+1 digits, then the | |
| decimal point, then the remaining | |
| digits without trailing zeroes. */ | |
| if (exponent >= 0) | |
| { | |
| /* Number of digits before the decimal point. */ | |
| size_t intpart_digits = exponent + 1; | |
| /* Note: intpart_digits <= precision = ndigits. */ | |
| const DCHAR_T *thousep = NULL; | |
| DCHAR_T thousep_buf[10]; | |
| # if !WIDE_CHAR_VERSION | |
| size_t thousep_len = 0; | |
| # endif | |
| const signed char *grouping; | |
| size_t insert = 0; | |
| if ((flags & FLAG_GROUP) && (intpart_digits > 1)) | |
| { | |
| /* Determine the thousands separator and | |
| the grouping rule of the current locale. */ | |
| # if WIDE_CHAR_VERSION | |
| /* DCHAR_T is wchar_t. */ | |
| thousep = thousands_separator_wchar (thousep_buf); | |
| # define thousep_len 1 | |
| # elif defined DCHAR_CONV_FROM_ENCODING | |
| /* DCHAR_T is uintN_t. */ | |
| thousep = thousands_separator_DCHAR (thousep_buf); | |
| thousep_len = DCHAR_STRLEN (thousep); | |
| # else | |
| /* DCHAR_T is char. */ | |
| thousep = thousands_separator_char (thousep_buf); | |
| thousep_len = strlen (thousep); | |
| # endif | |
| if (*thousep == 0) | |
| thousep = NULL; | |
| if (thousep != NULL) | |
| { | |
| grouping = grouping_rule (); | |
| insert = | |
| num_thousands_separators (grouping, intpart_digits); | |
| } | |
| } | |
| const char *digitp = digits + ndigits - intpart_digits; | |
| DCHAR_T *p_before_intpart = p; | |
| p += intpart_digits + insert * thousep_len; | |
| DCHAR_T *p_after_intpart = p; | |
| if (insert > 0) /* implies (flag & FLAG_GROUP) && (thousep != NULL) */ | |
| { | |
| const signed char *g = grouping; | |
| for (;;) | |
| { | |
| int h = *g; | |
| if (h <= 0) | |
| abort (); | |
| int i = h; | |
| do | |
| *--p = *digitp++; | |
| while (--i > 0); | |
| # if WIDE_CHAR_VERSION | |
| *--p = thousep[0]; | |
| # else | |
| p -= thousep_len; | |
| DCHAR_CPY (p, thousep, thousep_len); | |
| # endif | |
| insert--; | |
| if (insert == 0) | |
| break; | |
| if (g[1] != 0) | |
| g++; | |
| } | |
| } | |
| for (;;) | |
| { | |
| *--p = *digitp++; | |
| if (p == p_before_intpart) | |
| break; | |
| } | |
| p = p_after_intpart; | |
| ndigits -= intpart_digits; | |
| # undef thousep_len | |
| if ((flags & FLAG_ALT) || ndigits > nzeroes) | |
| { | |
| *p++ = decimal_point_char (); | |
| while (ndigits > nzeroes) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| size_t ecount = -exponent - 1; | |
| *p++ = '0'; | |
| *p++ = decimal_point_char (); | |
| for (; ecount > 0; ecount--) | |
| *p++ = '0'; | |
| while (ndigits > nzeroes) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| /* Exponential notation. */ | |
| *p++ = digits[--ndigits]; | |
| if ((flags & FLAG_ALT) || ndigits > nzeroes) | |
| { | |
| *p++ = decimal_point_char (); | |
| while (ndigits > nzeroes) | |
| { | |
| --ndigits; | |
| *p++ = digits[ndigits]; | |
| } | |
| } | |
| *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */ | |
| # if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR | |
| { | |
| static const wchar_t decimal_format[] = | |
| /* Produce the same number of exponent digits | |
| as the native printf implementation. */ | |
| # if defined _WIN32 && ! defined __CYGWIN__ | |
| { '%', '+', '.', '3', 'd', '\0' }; | |
| # else | |
| { '%', '+', '.', '2', 'd', '\0' }; | |
| # endif | |
| SNPRINTF (p, 6 + 1, decimal_format, exponent); | |
| } | |
| while (*p != '\0') | |
| p++; | |
| # else | |
| { | |
| static const char decimal_format[] = | |
| /* Produce the same number of exponent digits | |
| as the native printf implementation. */ | |
| # if defined _WIN32 && ! defined __CYGWIN__ | |
| "%+.3d"; | |
| # else | |
| "%+.2d"; | |
| # endif | |
| if (sizeof (DCHAR_T) == 1) | |
| { | |
| sprintf ((char *) p, decimal_format, exponent); | |
| while (*p != '\0') | |
| p++; | |
| } | |
| else | |
| { | |
| char expbuf[6 + 1]; | |
| sprintf (expbuf, decimal_format, exponent); | |
| for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) | |
| p++; | |
| } | |
| } | |
| # endif | |
| } | |
| free (digits); | |
| } | |
| } | |
| else | |
| abort (); | |
| # else | |
| /* arg is finite. */ | |
| if (!(arg == 0.0)) | |
| abort (); | |
| pad_ptr = p; | |
| if (dp->conversion == 'f' || dp->conversion == 'F') | |
| { | |
| *p++ = '0'; | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| for (; precision > 0; precision--) | |
| *p++ = '0'; | |
| } | |
| } | |
| else if (dp->conversion == 'e' || dp->conversion == 'E') | |
| { | |
| *p++ = '0'; | |
| if ((flags & FLAG_ALT) || precision > 0) | |
| { | |
| *p++ = decimal_point_char (); | |
| for (; precision > 0; precision--) | |
| *p++ = '0'; | |
| } | |
| *p++ = dp->conversion; /* 'e' or 'E' */ | |
| *p++ = '+'; | |
| /* Produce the same number of exponent digits as | |
| the native printf implementation. */ | |
| # if defined _WIN32 && ! defined __CYGWIN__ | |
| *p++ = '0'; | |
| # endif | |
| *p++ = '0'; | |
| *p++ = '0'; | |
| } | |
| else if (dp->conversion == 'g' || dp->conversion == 'G') | |
| { | |
| *p++ = '0'; | |
| if (flags & FLAG_ALT) | |
| { | |
| size_t ndigits = | |
| (precision > 0 ? precision - 1 : 0); | |
| *p++ = decimal_point_char (); | |
| for (; ndigits > 0; --ndigits) | |
| *p++ = '0'; | |
| } | |
| } | |
| else | |
| abort (); | |
| # endif | |
| } | |
| } | |
| } | |
| # endif | |
| /* The generated string now extends from tmp to p, with the | |
| zero padding insertion point being at pad_ptr. */ | |
| size_t count = p - tmp; | |
| if (count < width) | |
| { | |
| size_t pad = width - count; | |
| DCHAR_T *end = p + pad; | |
| if (flags & FLAG_LEFT) | |
| { | |
| /* Pad with spaces on the right. */ | |
| for (; pad > 0; pad--) | |
| *p++ = ' '; | |
| } | |
| else if ((flags & FLAG_ZERO) && pad_ptr != NULL) | |
| { | |
| /* Pad with zeroes. */ | |
| DCHAR_T *q = end; | |
| while (p > pad_ptr) | |
| *--q = *--p; | |
| for (; pad > 0; pad--) | |
| *p++ = '0'; | |
| } | |
| else | |
| { | |
| /* Pad with spaces on the left. */ | |
| DCHAR_T *q = end; | |
| while (p > tmp) | |
| *--q = *--p; | |
| for (; pad > 0; pad--) | |
| *p++ = ' '; | |
| } | |
| p = end; | |
| } | |
| count = p - tmp; | |
| if (count >= tmp_length) | |
| /* tmp_length was incorrectly calculated - fix the | |
| code above! */ | |
| abort (); | |
| /* Make room for the result. */ | |
| if (count >= allocated - length) | |
| { | |
| size_t n = xsum (length, count); | |
| ENSURE_ALLOCATION_ELSE (n, | |
| { if (tmp != tmpbuf) free (tmp); goto out_of_memory; }); | |
| } | |
| /* Append the result. */ | |
| memcpy (result + length, tmp, count * sizeof (DCHAR_T)); | |
| if (tmp != tmpbuf) | |
| free (tmp); | |
| length += count; | |
| } | |
| #endif | |
| else | |
| { | |
| arg_type type = a.arg[dp->arg_index].type; | |
| int flags = dp->flags; | |
| #if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION | |
| int prec_ourselves; | |
| #else | |
| # define prec_ourselves 0 | |
| #endif | |
| #if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| int group_ourselves; | |
| #else | |
| # define group_ourselves 0 | |
| #endif | |
| #if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST | |
| # define pad_ourselves 1 | |
| #elif !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| int pad_ourselves; | |
| #else | |
| # define pad_ourselves 0 | |
| #endif | |
| TCHAR_T *fbp; | |
| unsigned int prefix_count; | |
| int prefixes[2] IF_LINT (= { 0 }); | |
| int orig_errno; | |
| #if !USE_SNPRINTF | |
| size_t tmp_length; | |
| TCHAR_T tmpbuf[700]; | |
| TCHAR_T *tmp; | |
| #endif | |
| #if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| int has_width = 0; | |
| #endif | |
| #if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| size_t width = 0; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| flags |= FLAG_LEFT; | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| # define WIDTH_IS_CHECKED 1 | |
| # if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| has_width = 1; | |
| # endif | |
| } | |
| #endif | |
| #if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| int has_precision = 0; | |
| size_t precision = 6; | |
| if (dp->precision_start != dp->precision_end) | |
| { | |
| if (dp->precision_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->precision_arg_index].a.a_int; | |
| /* "A negative precision is taken as if the precision | |
| were omitted." */ | |
| if (arg >= 0) | |
| { | |
| precision = arg; | |
| has_precision = 1; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->precision_start + 1; | |
| precision = 0; | |
| while (digitp != dp->precision_end) | |
| precision = xsum (xtimes (precision, 10), *digitp++ - '0'); | |
| has_precision = 1; | |
| } | |
| } | |
| #endif | |
| /* Decide whether to handle the precision ourselves. */ | |
| #if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION | |
| switch (dp->conversion) | |
| { | |
| # if NEED_PRINTF_UNBOUNDED_PRECISION | |
| case 'd': case 'i': case 'u': | |
| case 'b': | |
| #if SUPPORT_GNU_PRINTF_DIRECTIVES \ | |
| || (__GLIBC__ + (__GLIBC_MINOR__ >= 35) > 2) | |
| case 'B': | |
| #endif | |
| case 'o': | |
| prec_ourselves = has_precision && (precision > 0); | |
| break; | |
| # endif | |
| case 'x': case 'X': case 'p': | |
| prec_ourselves = | |
| has_precision | |
| && (0 | |
| # if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO | |
| || (precision == 0) | |
| # endif | |
| # if NEED_PRINTF_UNBOUNDED_PRECISION | |
| || (precision > 0) | |
| # endif | |
| ); | |
| break; | |
| default: | |
| prec_ourselves = 0; | |
| break; | |
| } | |
| #endif | |
| /* Decide whether to add the thousands separators ourselves. */ | |
| #if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| if (flags & FLAG_GROUP) | |
| { | |
| switch (dp->conversion) | |
| { | |
| case 'd': case 'i': case 'u': | |
| # if NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| group_ourselves = 1; | |
| # else | |
| group_ourselves = prec_ourselves; | |
| # endif | |
| break; | |
| case 'f': case 'F': case 'g': case 'G': | |
| # if NEED_PRINTF_FLAG_GROUPING | |
| group_ourselves = 1; | |
| # else | |
| group_ourselves = prec_ourselves; | |
| # endif | |
| break; | |
| default: | |
| group_ourselves = 0; | |
| break; | |
| } | |
| } | |
| else | |
| group_ourselves = 0; | |
| #endif | |
| /* Decide whether to perform the padding ourselves. */ | |
| #if !((WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST) && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT) | |
| switch (dp->conversion) | |
| { | |
| # if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO | |
| # if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO | |
| /* If we need conversion from TCHAR_T[] to DCHAR_T[], we need | |
| to perform the padding after this conversion. Functions | |
| with unistdio extensions perform the padding based on | |
| character count rather than element count. */ | |
| case 'c': case 's': | |
| # endif | |
| # if NEED_PRINTF_FLAG_ZERO | |
| case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': | |
| case 'a': case 'A': | |
| # endif | |
| pad_ourselves = 1; | |
| break; | |
| # endif | |
| default: | |
| pad_ourselves = prec_ourselves | group_ourselves; | |
| break; | |
| } | |
| #endif | |
| #if !USE_SNPRINTF | |
| /* Allocate a temporary buffer of sufficient size for calling | |
| sprintf. */ | |
| tmp_length = | |
| MAX_ROOM_NEEDED (&a, dp->arg_index, dp->conversion, type, | |
| flags, width, has_precision, precision, | |
| pad_ourselves); | |
| if (tmp_length <= sizeof (tmpbuf) / sizeof (TCHAR_T)) | |
| tmp = tmpbuf; | |
| else | |
| { | |
| size_t tmp_memsize = xtimes (tmp_length, sizeof (TCHAR_T)); | |
| if (size_overflow_p (tmp_memsize)) | |
| /* Overflow, would lead to out of memory. */ | |
| goto out_of_memory; | |
| tmp = (TCHAR_T *) malloc (tmp_memsize); | |
| if (tmp == NULL) | |
| /* Out of memory. */ | |
| goto out_of_memory; | |
| } | |
| #endif | |
| /* Construct the format string for calling snprintf or | |
| sprintf. */ | |
| fbp = buf; | |
| *fbp++ = '%'; | |
| if ((flags & FLAG_GROUP) && !group_ourselves) | |
| *fbp++ = '\''; | |
| if (flags & FLAG_LEFT) | |
| *fbp++ = '-'; | |
| if (flags & FLAG_SHOWSIGN) | |
| *fbp++ = '+'; | |
| if (flags & FLAG_SPACE) | |
| *fbp++ = ' '; | |
| if (flags & FLAG_ALT) | |
| *fbp++ = '#'; | |
| #if __GLIBC__ >= 2 && !defined __UCLIBC__ | |
| if (flags & FLAG_LOCALIZED) | |
| *fbp++ = 'I'; | |
| #endif | |
| if (!pad_ourselves) | |
| { | |
| if (flags & FLAG_ZERO) | |
| *fbp++ = '0'; | |
| if (dp->width_start != dp->width_end) | |
| { | |
| size_t n = dp->width_end - dp->width_start; | |
| #if !WIDTH_IS_CHECKED | |
| size_t width; | |
| /* Reject an out-of-range width. | |
| The underlying SNPRINTF already does this on some | |
| platforms (glibc, musl, macOS, FreeBSD, NetBSD, | |
| OpenBSD, Cygwin, Solaris, MSVC). However, on others | |
| (AIX, mingw), it doesn't; thus this vasnprintf | |
| invocation would succeed and produce a wrong result. | |
| So, this is redundant on some platforms, but it's a | |
| quick check anyway. */ | |
| if (dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| int arg = a.arg[dp->width_arg_index].a.a_int; | |
| width = arg; | |
| if (arg < 0) | |
| { | |
| /* "A negative field width is taken as a '-' flag | |
| followed by a positive field width." */ | |
| width = -width; | |
| } | |
| } | |
| else | |
| { | |
| const FCHAR_T *digitp = dp->width_start; | |
| width = 0; | |
| do | |
| width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
| while (digitp != dp->width_end); | |
| } | |
| if (width > (size_t) INT_MAX) | |
| goto overflow; | |
| #endif | |
| /* The width specification is known to consist only | |
| of standard ASCII characters. */ | |
| if (sizeof (FCHAR_T) == sizeof (TCHAR_T)) | |
| { | |
| memcpy (fbp, dp->width_start, n * sizeof (TCHAR_T)); | |
| fbp += n; | |
| } | |
| else | |
| { | |
| const FCHAR_T *mp = dp->width_start; | |
| do | |
| *fbp++ = *mp++; | |
| while (--n > 0); | |
| } | |
| } | |
| } | |
| if (!prec_ourselves) | |
| { | |
| if (dp->precision_start != dp->precision_end) | |
| { | |
| size_t n = dp->precision_end - dp->precision_start; | |
| /* The precision specification is known to consist only | |
| of standard ASCII characters. */ | |
| if (sizeof (FCHAR_T) == sizeof (TCHAR_T)) | |
| { | |
| memcpy (fbp, dp->precision_start, n * sizeof (TCHAR_T)); | |
| fbp += n; | |
| } | |
| else | |
| { | |
| const FCHAR_T *mp = dp->precision_start; | |
| do | |
| *fbp++ = *mp++; | |
| while (--n > 0); | |
| } | |
| } | |
| } | |
| switch (+type) | |
| { | |
| case TYPE_LONGLONGINT: | |
| case TYPE_ULONGLONGINT: | |
| #if INT8_WIDTH > LONG_WIDTH | |
| case TYPE_INT8_T: | |
| #endif | |
| #if UINT8_WIDTH > LONG_WIDTH | |
| case TYPE_UINT8_T: | |
| #endif | |
| #if INT16_WIDTH > LONG_WIDTH | |
| case TYPE_INT16_T: | |
| #endif | |
| #if UINT16_WIDTH > LONG_WIDTH | |
| case TYPE_UINT16_T: | |
| #endif | |
| #if INT32_WIDTH > LONG_WIDTH | |
| case TYPE_INT32_T: | |
| #endif | |
| #if UINT32_WIDTH > LONG_WIDTH | |
| case TYPE_UINT32_T: | |
| #endif | |
| #if INT64_WIDTH > LONG_WIDTH | |
| case TYPE_INT64_T: | |
| #endif | |
| #if UINT64_WIDTH > LONG_WIDTH | |
| case TYPE_UINT64_T: | |
| #endif | |
| #if INT_FAST8_WIDTH > LONG_WIDTH | |
| case TYPE_INT_FAST8_T: | |
| #endif | |
| #if UINT_FAST8_WIDTH > LONG_WIDTH | |
| case TYPE_UINT_FAST8_T: | |
| #endif | |
| #if INT_FAST16_WIDTH > LONG_WIDTH | |
| case TYPE_INT_FAST16_T: | |
| #endif | |
| #if UINT_FAST16_WIDTH > LONG_WIDTH | |
| case TYPE_UINT_FAST16_T: | |
| #endif | |
| #if INT_FAST32_WIDTH > LONG_WIDTH | |
| case TYPE_INT3_FAST2_T: | |
| #endif | |
| #if UINT_FAST32_WIDTH > LONG_WIDTH | |
| case TYPE_UINT_FAST32_T: | |
| #endif | |
| #if INT_FAST64_WIDTH > LONG_WIDTH | |
| case TYPE_INT_FAST64_T: | |
| #endif | |
| #if UINT_FAST64_WIDTH > LONG_WIDTH | |
| case TYPE_UINT_FAST64_T: | |
| #endif | |
| #if defined _WIN32 && ! defined __CYGWIN__ | |
| *fbp++ = 'I'; | |
| *fbp++ = '6'; | |
| *fbp++ = '4'; | |
| break; | |
| #else | |
| *fbp++ = 'l'; | |
| #endif | |
| FALLTHROUGH; | |
| case TYPE_LONGINT: | |
| case TYPE_ULONGINT: | |
| #if INT8_WIDTH > INT_WIDTH && INT8_WIDTH <= LONG_WIDTH | |
| case TYPE_INT8_T: | |
| #endif | |
| #if UINT8_WIDTH > INT_WIDTH && UINT8_WIDTH <= LONG_WIDTH | |
| case TYPE_UINT8_T: | |
| #endif | |
| #if INT16_WIDTH > INT_WIDTH && INT16_WIDTH <= LONG_WIDTH | |
| case TYPE_INT16_T: | |
| #endif | |
| #if UINT16_WIDTH > INT_WIDTH && UINT16_WIDTH <= LONG_WIDTH | |
| case TYPE_UINT16_T: | |
| #endif | |
| #if INT32_WIDTH > INT_WIDTH && INT32_WIDTH <= LONG_WIDTH | |
| case TYPE_INT32_T: | |
| #endif | |
| #if UINT32_WIDTH > INT_WIDTH && UINT32_WIDTH <= LONG_WIDTH | |
| case TYPE_UINT32_T: | |
| #endif | |
| #if INT64_WIDTH > INT_WIDTH && INT64_WIDTH <= LONG_WIDTH | |
| case TYPE_INT64_T: | |
| #endif | |
| #if UINT64_WIDTH > INT_WIDTH && UINT64_WIDTH <= LONG_WIDTH | |
| case TYPE_UINT64_T: | |
| #endif | |
| #if INT_FAST8_WIDTH > INT_WIDTH && INT_FAST8_WIDTH <= LONG_WIDTH | |
| case TYPE_INT_FAST8_T: | |
| #endif | |
| #if UINT_FAST8_WIDTH > INT_WIDTH && UINT_FAST8_WIDTH <= LONG_WIDTH | |
| case TYPE_UINT_FAST8_T: | |
| #endif | |
| #if INT_FAST16_WIDTH > INT_WIDTH && INT_FAST16_WIDTH <= LONG_WIDTH | |
| case TYPE_INT_FAST16_T: | |
| #endif | |
| #if UINT_FAST16_WIDTH > INT_WIDTH && UINT_FAST16_WIDTH <= LONG_WIDTH | |
| case TYPE_UINT_FAST16_T: | |
| #endif | |
| #if INT_FAST32_WIDTH > INT_WIDTH && INT_FAST32_WIDTH <= LONG_WIDTH | |
| case TYPE_INT_FAST32_T: | |
| #endif | |
| #if UINT_FAST32_WIDTH > INT_WIDTH && UINT_FAST32_WIDTH <= LONG_WIDTH | |
| case TYPE_UINT_FAST32_T: | |
| #endif | |
| #if INT_FAST64_WIDTH > INT_WIDTH && INT_FAST64_WIDTH <= LONG_WIDTH | |
| case TYPE_INT_FAST64_T: | |
| #endif | |
| #if UINT_FAST64_WIDTH > INT_WIDTH && UINT_FAST64_WIDTH <= LONG_WIDTH | |
| case TYPE_UINT_FAST64_T: | |
| #endif | |
| #if HAVE_WINT_T | |
| case TYPE_WIDE_CHAR: | |
| #endif | |
| case TYPE_WIDE_STRING: | |
| *fbp++ = 'l'; | |
| break; | |
| case TYPE_LONGDOUBLE: | |
| *fbp++ = 'L'; | |
| break; | |
| default: | |
| break; | |
| } | |
| #if NEED_PRINTF_DIRECTIVE_F | |
| if (dp->conversion == 'F') | |
| *fbp = 'f'; | |
| else | |
| #endif | |
| *fbp = dp->conversion; | |
| #if USE_SNPRINTF | |
| /* Decide whether to pass %n in the format string | |
| to SNPRINTF. */ | |
| # if (((!WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR) \ | |
| && (HAVE_SNPRINTF_RETVAL_C99 && HAVE_SNPRINTF_TRUNCATION_C99)) \ | |
| || ((__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) \ | |
| && !defined __UCLIBC__) \ | |
| || (defined __APPLE__ && defined __MACH__) \ | |
| || defined __OpenBSD__ \ | |
| || defined __ANDROID__ \ | |
| || (defined _WIN32 && ! defined __CYGWIN__)) \ | |
| || (WIDE_CHAR_VERSION && MUSL_LIBC) | |
| /* We can avoid passing %n and instead rely on SNPRINTF's | |
| return value if | |
| - !WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR, because otherwise, | |
| when WIDE_CHAR_VERSION && DCHAR_IS_TCHAR, | |
| snwprintf()/_snwprintf() (Windows) and swprintf() (Unix) | |
| don't return the needed buffer size, | |
| and | |
| - we're compiling for a system where we know | |
| - that snprintf's return value conforms to ISO C 99 | |
| (HAVE_SNPRINTF_RETVAL_C99) and | |
| - that snprintf always produces NUL-terminated strings | |
| (HAVE_SNPRINTF_TRUNCATION_C99). | |
| And it is desirable to do so, because more and more platforms | |
| no longer support %n, for "security reasons". */ | |
| /* On specific platforms, listed below, we *must* avoid %n. | |
| In the case | |
| !WIDE_CHAR_VERSION && HAVE_SNPRINTF_RETVAL_C99 && !USE_MSVC__SNPRINTF | |
| we can rely on the return value of snprintf instead. Whereas | |
| in the opposite case | |
| WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF | |
| we need to make room based on an estimation, computed by | |
| MAX_ROOM_NEEDED. */ | |
| /* The following platforms forbid %n: | |
| - On glibc2 systems from 2004-10-18 or newer, the use of | |
| %n in format strings in writable memory may crash the | |
| program (if compiled with _FORTIFY_SOURCE >= 2). | |
| - On macOS 10.13 or newer, the use of %n in format | |
| strings in writable memory by default crashes the | |
| program. | |
| - On OpenBSD, since 2021-08-30, the use of %n in format | |
| strings produces an abort (see | |
| <https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdio/vfprintf.c.diff?r1=1.79&r2=1.80&f=h>, | |
| <https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdio/vfwprintf.c.diff?r1=1.20&r2=1.21&f=h>). | |
| - On Android, starting on 2018-03-07, the use of %n in | |
| format strings produces a fatal error (see | |
| <https://android.googlesource.com/platform/bionic/+/41398d03b7e8e0dfb951660ae713e682e9fc0336>). | |
| - On native Windows systems (such as mingw) where the OS is | |
| Windows Vista, the use of %n in format strings by default | |
| crashes the program. See | |
| <https://gcc.gnu.org/ml/gcc/2007-06/msg00122.html> and | |
| <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/set-printf-count-output> | |
| On the first four of these platforms, if !WIDE_CHAR_VERSION, | |
| it is not a big deal to avoid %n, because on these platforms, | |
| HAVE_SNPRINTF_RETVAL_C99 and HAVE_SNPRINTF_TRUNCATION_C99 are | |
| 1. | |
| On native Windows, if !WIDE_CHAR_VERSION, it's not a big deal | |
| either because: | |
| - Although the gl_SNPRINTF_TRUNCATION_C99 test fails, | |
| snprintf does not write more than the specified number | |
| of bytes. (snprintf (buf, 3, "%d %d", 4567, 89) writes | |
| '4', '5', '6' into buf, not '4', '5', '\0'.) | |
| - Although the gl_SNPRINTF_RETVAL_C99 test fails, snprintf | |
| allows us to recognize the case of an insufficient | |
| buffer size: it returns -1 in this case. */ | |
| /* Additionally, in the WIDE_CHAR_VERSION case, we cannot use %n | |
| on musl libc because we would run into an swprintf() bug. | |
| See <https://www.openwall.com/lists/musl/2023/03/19/1>. */ | |
| fbp[1] = '\0'; | |
| # else /* AIX <= 5.1, HP-UX, Solaris <= 9, BeOS */ | |
| fbp[1] = '%'; | |
| fbp[2] = 'n'; | |
| fbp[3] = '\0'; | |
| # endif | |
| #else | |
| fbp[1] = '\0'; | |
| #endif | |
| /* Construct the arguments for calling snprintf or sprintf. */ | |
| prefix_count = 0; | |
| if (!pad_ourselves && dp->width_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
| abort (); | |
| prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int; | |
| } | |
| if (!prec_ourselves && dp->precision_arg_index != ARG_NONE) | |
| { | |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
| abort (); | |
| prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int; | |
| } | |
| #if USE_SNPRINTF | |
| /* The SNPRINTF result is appended after result[0..length]. | |
| The latter is an array of DCHAR_T; SNPRINTF appends an | |
| array of TCHAR_T to it. This is possible because | |
| sizeof (TCHAR_T) divides sizeof (DCHAR_T) and | |
| alignof (TCHAR_T) <= alignof (DCHAR_T). */ | |
| # define TCHARS_PER_DCHAR (sizeof (DCHAR_T) / sizeof (TCHAR_T)) | |
| /* Ensure that maxlen below will be >= 2. Needed on BeOS, | |
| where an snprintf() with maxlen==1 acts like sprintf(). */ | |
| ENSURE_ALLOCATION (xsum (length, | |
| (2 + TCHARS_PER_DCHAR - 1) | |
| / TCHARS_PER_DCHAR)); | |
| /* Prepare checking whether snprintf returns the count | |
| via %n. */ | |
| *(TCHAR_T *) (result + length) = '\0'; | |
| #endif | |
| orig_errno = errno; | |
| for (;;) | |
| { | |
| int count = -1; | |
| #if USE_SNPRINTF | |
| int retcount = 0; | |
| size_t maxlen = allocated - length; | |
| /* SNPRINTF can fail if its second argument is | |
| > INT_MAX. */ | |
| if (maxlen > INT_MAX / TCHARS_PER_DCHAR) | |
| maxlen = INT_MAX / TCHARS_PER_DCHAR; | |
| maxlen = maxlen * TCHARS_PER_DCHAR; | |
| # define SNPRINTF_BUF(arg) \ | |
| switch (prefix_count) \ | |
| { \ | |
| case 0: \ | |
| retcount = SNPRINTF ((TCHAR_T *) (result + length), \ | |
| maxlen, buf, \ | |
| arg, &count); \ | |
| break; \ | |
| case 1: \ | |
| retcount = SNPRINTF ((TCHAR_T *) (result + length), \ | |
| maxlen, buf, \ | |
| prefixes[0], arg, &count); \ | |
| break; \ | |
| case 2: \ | |
| retcount = SNPRINTF ((TCHAR_T *) (result + length), \ | |
| maxlen, buf, \ | |
| prefixes[0], prefixes[1], arg, \ | |
| &count); \ | |
| break; \ | |
| default: \ | |
| abort (); \ | |
| } | |
| #else | |
| # define SNPRINTF_BUF(arg) \ | |
| switch (prefix_count) \ | |
| { \ | |
| case 0: \ | |
| count = sprintf (tmp, buf, arg); \ | |
| break; \ | |
| case 1: \ | |
| count = sprintf (tmp, buf, prefixes[0], arg); \ | |
| break; \ | |
| case 2: \ | |
| count = sprintf (tmp, buf, prefixes[0], prefixes[1],\ | |
| arg); \ | |
| break; \ | |
| default: \ | |
| abort (); \ | |
| } | |
| #endif | |
| errno = 0; | |
| switch (+type) | |
| { | |
| case TYPE_SCHAR: | |
| { | |
| int arg = a.arg[dp->arg_index].a.a_schar; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UCHAR: | |
| { | |
| unsigned int arg = a.arg[dp->arg_index].a.a_uchar; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_SHORT: | |
| { | |
| int arg = a.arg[dp->arg_index].a.a_short; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_USHORT: | |
| { | |
| unsigned int arg = a.arg[dp->arg_index].a.a_ushort; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_INT: | |
| { | |
| int arg = a.arg[dp->arg_index].a.a_int; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UINT: | |
| { | |
| unsigned int arg = a.arg[dp->arg_index].a.a_uint; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_LONGINT: | |
| { | |
| long int arg = a.arg[dp->arg_index].a.a_longint; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_ULONGINT: | |
| { | |
| unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_LONGLONGINT: | |
| { | |
| long long int arg = a.arg[dp->arg_index].a.a_longlongint; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_ULONGLONGINT: | |
| { | |
| unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_INT8_T: | |
| { | |
| int8_t arg = a.arg[dp->arg_index].a.a_int8_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UINT8_T: | |
| { | |
| uint8_t arg = a.arg[dp->arg_index].a.a_uint8_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_INT16_T: | |
| { | |
| int16_t arg = a.arg[dp->arg_index].a.a_int16_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UINT16_T: | |
| { | |
| uint16_t arg = a.arg[dp->arg_index].a.a_uint16_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_INT32_T: | |
| { | |
| int32_t arg = a.arg[dp->arg_index].a.a_int32_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UINT32_T: | |
| { | |
| uint32_t arg = a.arg[dp->arg_index].a.a_uint32_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_INT64_T: | |
| { | |
| int64_t arg = a.arg[dp->arg_index].a.a_int64_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UINT64_T: | |
| { | |
| uint64_t arg = a.arg[dp->arg_index].a.a_uint64_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_INT_FAST8_T: | |
| { | |
| int_fast8_t arg = a.arg[dp->arg_index].a.a_int_fast8_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UINT_FAST8_T: | |
| { | |
| uint_fast8_t arg = a.arg[dp->arg_index].a.a_uint_fast8_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_INT_FAST16_T: | |
| { | |
| int_fast16_t arg = a.arg[dp->arg_index].a.a_int_fast16_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UINT_FAST16_T: | |
| { | |
| uint_fast16_t arg = a.arg[dp->arg_index].a.a_uint_fast16_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_INT_FAST32_T: | |
| { | |
| int_fast32_t arg = a.arg[dp->arg_index].a.a_int_fast32_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UINT_FAST32_T: | |
| { | |
| uint_fast32_t arg = a.arg[dp->arg_index].a.a_uint_fast32_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_INT_FAST64_T: | |
| { | |
| int_fast64_t arg = a.arg[dp->arg_index].a.a_int_fast64_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_UINT_FAST64_T: | |
| { | |
| uint_fast64_t arg = a.arg[dp->arg_index].a.a_uint_fast64_t; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_DOUBLE: | |
| { | |
| double arg = a.arg[dp->arg_index].a.a_double; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_LONGDOUBLE: | |
| { | |
| long double arg = a.arg[dp->arg_index].a.a_longdouble; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_CHAR: | |
| { | |
| int arg = a.arg[dp->arg_index].a.a_char; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| #if HAVE_WINT_T | |
| case TYPE_WIDE_CHAR: | |
| { | |
| wint_t arg = a.arg[dp->arg_index].a.a_wide_char; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| #endif | |
| case TYPE_STRING: | |
| { | |
| const char *arg = a.arg[dp->arg_index].a.a_string; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_WIDE_STRING: | |
| { | |
| const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| case TYPE_POINTER: | |
| { | |
| void *arg = a.arg[dp->arg_index].a.a_pointer; | |
| SNPRINTF_BUF (arg); | |
| } | |
| break; | |
| default: | |
| abort (); | |
| } | |
| #if USE_SNPRINTF | |
| /* Portability: Not all implementations of snprintf() | |
| are ISO C 99 compliant. Determine the number of | |
| bytes that snprintf() has produced or would have | |
| produced. */ | |
| if (count >= 0) | |
| { | |
| /* Verify that snprintf() has NUL-terminated its | |
| result. */ | |
| if ((unsigned int) count < maxlen | |
| && ((TCHAR_T *) (result + length)) [count] != '\0') | |
| abort (); | |
| /* Portability hack. */ | |
| if (retcount > count) | |
| count = retcount; | |
| } | |
| else | |
| { | |
| /* snprintf() doesn't understand the '%n' | |
| directive. */ | |
| if (fbp[1] != '\0') | |
| { | |
| /* Don't use the '%n' directive; instead, look | |
| at the snprintf() return value. */ | |
| fbp[1] = '\0'; | |
| continue; | |
| } | |
| else | |
| { | |
| /* Look at the snprintf() return value. */ | |
| if (retcount < 0) | |
| { | |
| # if (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF | |
| /* HP-UX 10.20 snprintf() is doubly deficient: | |
| It doesn't understand the '%n' directive, | |
| *and* it returns -1 (rather than the length | |
| that would have been required) when the | |
| buffer is too small. | |
| Likewise, in case of | |
| WIDE_CHAR_VERSION && DCHAR_IS_TCHAR, the | |
| functions snwprintf()/_snwprintf() (Windows) | |
| or swprintf() (Unix). | |
| But a failure at this point can also come | |
| from other reasons than a too small buffer, | |
| such as an invalid wide string argument to | |
| the %ls directive, or possibly an invalid | |
| floating-point argument. */ | |
| size_t tmp_length = | |
| MAX_ROOM_NEEDED (&a, dp->arg_index, | |
| dp->conversion, type, flags, | |
| width, | |
| has_precision, | |
| precision, pad_ourselves); | |
| if (maxlen < tmp_length) | |
| { | |
| /* Make more room. But try to do through | |
| this reallocation only once. */ | |
| size_t bigger_need = | |
| xsum (length, | |
| xsum (tmp_length, | |
| TCHARS_PER_DCHAR - 1) | |
| / TCHARS_PER_DCHAR); | |
| /* And always grow proportionally. | |
| (There may be several arguments, each | |
| needing a little more room than the | |
| previous one.) */ | |
| size_t bigger_need2 = | |
| xsum (xtimes (allocated, 2), 12); | |
| if (bigger_need < bigger_need2) | |
| bigger_need = bigger_need2; | |
| ENSURE_ALLOCATION (bigger_need); | |
| continue; | |
| } | |
| # endif | |
| } | |
| else | |
| { | |
| count = retcount; | |
| # if WIDE_CHAR_VERSION && defined __MINGW32__ | |
| if (count == 0 && dp->conversion == 'c') | |
| /* snwprintf returned 0 instead of 1. But it | |
| wrote a null wide character. */ | |
| count = 1; | |
| # endif | |
| } | |
| } | |
| } | |
| #endif | |
| /* Attempt to handle failure. */ | |
| if (count < 0) | |
| { | |
| /* SNPRINTF or sprintf failed. Use the errno that it | |
| has set, if any. */ | |
| if (errno == 0) | |
| { | |
| if (dp->conversion == 'c' || dp->conversion == 's') | |
| errno = EILSEQ; | |
| else | |
| errno = EINVAL; | |
| } | |
| goto fail_with_errno; | |
| } | |
| #if USE_SNPRINTF | |
| /* Handle overflow of the allocated buffer. | |
| If such an overflow occurs, a C99 compliant snprintf() | |
| returns a count >= maxlen. However, a non-compliant | |
| snprintf() function returns only count = maxlen - 1. To | |
| cover both cases, test whether count >= maxlen - 1. */ | |
| if ((unsigned int) count + 1 >= maxlen) | |
| { | |
| /* If maxlen already has attained its allowed maximum, | |
| allocating more memory will not increase maxlen. | |
| Instead of looping, bail out. */ | |
| if (maxlen == INT_MAX / TCHARS_PER_DCHAR) | |
| goto overflow; | |
| else | |
| { | |
| /* Need at least (count + 1) * sizeof (TCHAR_T) | |
| bytes. (The +1 is for the trailing NUL.) | |
| But ask for (count + 2) * sizeof (TCHAR_T) | |
| bytes, so that in the next round, we likely get | |
| maxlen > (unsigned int) count + 1 | |
| and so we don't get here again. | |
| And allocate proportionally, to avoid looping | |
| eternally if snprintf() reports a too small | |
| count. */ | |
| size_t n = | |
| xmax (xsum (length, | |
| ((unsigned int) count + 2 | |
| + TCHARS_PER_DCHAR - 1) | |
| / TCHARS_PER_DCHAR), | |
| xtimes (allocated, 2)); | |
| ENSURE_ALLOCATION (n); | |
| continue; | |
| } | |
| } | |
| #endif | |
| #if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION | |
| if (prec_ourselves) | |
| { | |
| /* Handle the precision. */ | |
| TCHAR_T *prec_ptr = | |
| # if USE_SNPRINTF | |
| (TCHAR_T *) (result + length); | |
| # else | |
| tmp; | |
| # endif | |
| size_t prefix_count; | |
| size_t move; | |
| prefix_count = 0; | |
| /* Put the additional zeroes after the sign. */ | |
| if (count >= 1 | |
| && (*prec_ptr == '-' || *prec_ptr == '+' | |
| || *prec_ptr == ' ')) | |
| prefix_count = 1; | |
| /* Put the additional zeroes after the 0x prefix if | |
| (flags & FLAG_ALT) || (dp->conversion == 'p'), or | |
| after the 0b prefix if (flags & FLAG_ALT). */ | |
| else if (count >= 2 | |
| && prec_ptr[0] == '0' | |
| && (prec_ptr[1] == 'x' || prec_ptr[1] == 'X' | |
| || prec_ptr[1] == 'b' | |
| || prec_ptr[1] == 'B')) | |
| prefix_count = 2; | |
| move = count - prefix_count; | |
| if (precision > move) | |
| { | |
| /* Insert zeroes. */ | |
| size_t insert = precision - move; | |
| TCHAR_T *prec_end; | |
| # if USE_SNPRINTF | |
| size_t n = | |
| xsum (length, | |
| (count + insert + TCHARS_PER_DCHAR - 1) | |
| / TCHARS_PER_DCHAR); | |
| length += (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR; | |
| ENSURE_ALLOCATION (n); | |
| length -= (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR; | |
| prec_ptr = (TCHAR_T *) (result + length); | |
| # endif | |
| prec_end = prec_ptr + count; | |
| prec_ptr += prefix_count; | |
| while (prec_end > prec_ptr) | |
| { | |
| prec_end--; | |
| prec_end[insert] = prec_end[0]; | |
| } | |
| prec_end += insert; | |
| do | |
| *--prec_end = '0'; | |
| while (prec_end > prec_ptr); | |
| count += insert; | |
| } | |
| # if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO | |
| else if (precision == 0 | |
| && move == 1 | |
| && prec_ptr[prefix_count] == '0') | |
| { | |
| /* Replace the "0" result with an empty string. */ | |
| count = prefix_count; | |
| } | |
| # endif | |
| } | |
| #endif | |
| #if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| if (group_ourselves) /* implies (flags & FLAG_GROUP) */ | |
| /* Handle the grouping. */ | |
| switch (dp->conversion) | |
| { | |
| /* These are the only conversion to which grouping | |
| applies. */ | |
| case 'd': case 'i': case 'u': | |
| case 'f': case 'F': case 'g': case 'G': | |
| { | |
| /* Determine the thousands separator of the current | |
| locale. */ | |
| const TCHAR_T *thousep; | |
| TCHAR_T thousep_buf[10]; | |
| # if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR | |
| /* TCHAR_T is wchar_t. */ | |
| thousep = thousands_separator_wchar (thousep_buf); | |
| # else | |
| /* TCHAR_T is char. */ | |
| thousep = thousands_separator_char (thousep_buf); | |
| # endif | |
| /* Nothing to do in locales where thousep is the empty | |
| string. */ | |
| if (*thousep != 0) | |
| { | |
| /* Since FLAG_LOCALIZED is only supported on glibc | |
| systems, here we can assume that all digits are | |
| the ASCII digits '0'..'9'. */ | |
| TCHAR_T *number_ptr = | |
| # if USE_SNPRINTF | |
| (TCHAR_T *) (result + length); | |
| # else | |
| tmp; | |
| # endif | |
| TCHAR_T *end_ptr = number_ptr + count; | |
| /* Find where the leading digits start. */ | |
| TCHAR_T *digits_ptr = number_ptr; | |
| if (count >= 1 | |
| && (*digits_ptr == '-' || *digits_ptr == '+' | |
| || *digits_ptr == ' ')) | |
| digits_ptr++; | |
| /* Find where the leading digits end. */ | |
| TCHAR_T *digits_end_ptr; | |
| switch (dp->conversion) | |
| { | |
| case 'd': case 'i': case 'u': | |
| digits_end_ptr = end_ptr; | |
| break; | |
| case 'f': case 'F': case 'g': case 'G': | |
| { | |
| TCHAR_T decimal_point = decimal_point_char (); | |
| for (digits_end_ptr = digits_ptr; | |
| digits_end_ptr < end_ptr; | |
| digits_end_ptr++) | |
| if (*digits_end_ptr == decimal_point | |
| || *digits_end_ptr == 'e') | |
| break; | |
| } | |
| break; | |
| } | |
| /* Determine the number of thousands separators | |
| to insert. */ | |
| const signed char *grouping = grouping_rule (); | |
| size_t insert = | |
| num_thousands_separators (grouping, digits_end_ptr - digits_ptr); | |
| if (insert > 0) | |
| { | |
| # if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR | |
| # define thousep_len 1 | |
| # else | |
| size_t thousep_len = strlen (thousep); | |
| # endif | |
| # if USE_SNPRINTF | |
| size_t digits_offset = digits_ptr - number_ptr; | |
| size_t digits_end_offset = digits_end_ptr - number_ptr; | |
| size_t n = | |
| xsum (length, | |
| (count + insert * thousep_len + TCHARS_PER_DCHAR - 1) | |
| / TCHARS_PER_DCHAR); | |
| length += (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR; | |
| ENSURE_ALLOCATION (n); | |
| length -= (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR; | |
| number_ptr = (TCHAR_T *) (result + length); | |
| end_ptr = number_ptr + count; | |
| digits_ptr = number_ptr + digits_offset; | |
| digits_end_ptr = number_ptr + digits_end_offset; | |
| # endif | |
| count += insert * thousep_len; | |
| const TCHAR_T *p = end_ptr; | |
| TCHAR_T *q = end_ptr + insert * thousep_len; | |
| while (p > digits_end_ptr) | |
| *--q = *--p; | |
| const signed char *g = grouping; | |
| for (;;) | |
| { | |
| int h = *g; | |
| if (h <= 0) | |
| abort (); | |
| int i = h; | |
| do | |
| *--q = *--p; | |
| while (--i > 0); | |
| # if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR | |
| *--q = *thousep; | |
| # else | |
| q -= thousep_len; | |
| memcpy (q, thousep, thousep_len); | |
| # endif | |
| insert--; | |
| if (insert == 0) | |
| break; | |
| if (g[1] != 0) | |
| g++; | |
| } | |
| /* Here q == p. Done with the insertions. */ | |
| } | |
| } | |
| } | |
| break; | |
| } | |
| #endif | |
| #if !USE_SNPRINTF | |
| if (count >= tmp_length) | |
| /* tmp_length was incorrectly calculated - fix the | |
| code above! */ | |
| abort (); | |
| #endif | |
| #if !DCHAR_IS_TCHAR | |
| /* Convert from TCHAR_T[] to DCHAR_T[]. */ | |
| if (dp->conversion == 'c' || dp->conversion == 's' | |
| || (flags & FLAG_GROUP) | |
| # if __GLIBC__ >= 2 && !defined __UCLIBC__ | |
| || (flags & FLAG_LOCALIZED) | |
| # endif | |
| ) | |
| { | |
| /* The result string is not guaranteed to be ASCII. */ | |
| /* This code assumes that TCHAR_T is 'char'. */ | |
| static_assert (sizeof (TCHAR_T) == 1); | |
| const TCHAR_T *tmpsrc; | |
| # if USE_SNPRINTF | |
| tmpsrc = (TCHAR_T *) (result + length); | |
| # else | |
| tmpsrc = tmp; | |
| # endif | |
| DCHAR_T *tmpdst; | |
| size_t tmpdst_len; | |
| # if WIDE_CHAR_VERSION | |
| /* Convert tmpsrc[0..count-1] to a freshly allocated | |
| wide character array. */ | |
| mbstate_t state; | |
| mbszero (&state); | |
| tmpdst_len = 0; | |
| { | |
| const TCHAR_T *src = tmpsrc; | |
| size_t srclen = count; | |
| for (; srclen > 0; tmpdst_len++) | |
| { | |
| /* Parse the next multibyte character. */ | |
| size_t ret = mbrtowc (NULL, src, srclen, &state); | |
| if (ret == (size_t)(-2) || ret == (size_t)(-1)) | |
| goto fail_with_EILSEQ; | |
| if (ret == 0) | |
| ret = 1; | |
| src += ret; | |
| srclen -= ret; | |
| } | |
| } | |
| tmpdst = | |
| (wchar_t *) malloc ((tmpdst_len + 1) * sizeof (wchar_t)); | |
| if (tmpdst == NULL) | |
| goto out_of_memory; | |
| mbszero (&state); | |
| { | |
| DCHAR_T *destptr = tmpdst; | |
| const TCHAR_T *src = tmpsrc; | |
| size_t srclen = count; | |
| for (; srclen > 0; destptr++) | |
| { | |
| /* Parse the next multibyte character. */ | |
| size_t ret = mbrtowc (destptr, src, srclen, &state); | |
| if (ret == (size_t)(-2) || ret == (size_t)(-1)) | |
| /* Should already have been caught in the first | |
| loop, above. */ | |
| abort (); | |
| if (ret == 0) | |
| ret = 1; | |
| src += ret; | |
| srclen -= ret; | |
| } | |
| } | |
| # else | |
| tmpdst = | |
| DCHAR_CONV_FROM_ENCODING (locale_charset (), | |
| iconveh_question_mark, | |
| tmpsrc, count, | |
| NULL, | |
| NULL, &tmpdst_len); | |
| if (tmpdst == NULL) | |
| goto fail_with_errno; | |
| # endif | |
| ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len), | |
| { free (tmpdst); goto out_of_memory; }); | |
| DCHAR_CPY (result + length, tmpdst, tmpdst_len); | |
| free (tmpdst); | |
| count = tmpdst_len; | |
| } | |
| else | |
| { | |
| /* The result string is ASCII. | |
| Simple 1:1 conversion. */ | |
| # if USE_SNPRINTF | |
| /* If sizeof (DCHAR_T) == sizeof (TCHAR_T), it's a | |
| no-op conversion, in-place on the array starting | |
| at (result + length). */ | |
| if (sizeof (DCHAR_T) != sizeof (TCHAR_T)) | |
| # endif | |
| { | |
| const TCHAR_T *tmpsrc; | |
| # if USE_SNPRINTF | |
| if (result == resultbuf) | |
| { | |
| tmpsrc = (TCHAR_T *) (result + length); | |
| /* ENSURE_ALLOCATION will not move tmpsrc | |
| (because it's part of resultbuf). */ | |
| ENSURE_ALLOCATION (xsum (length, count)); | |
| } | |
| else | |
| { | |
| /* ENSURE_ALLOCATION will move the array | |
| (because it uses realloc(). */ | |
| ENSURE_ALLOCATION (xsum (length, count)); | |
| tmpsrc = (TCHAR_T *) (result + length); | |
| } | |
| # else | |
| tmpsrc = tmp; | |
| ENSURE_ALLOCATION (xsum (length, count)); | |
| # endif | |
| DCHAR_T *tmpdst = result + length; | |
| /* Copy backwards, because of overlapping. */ | |
| tmpsrc += count; | |
| tmpdst += count; | |
| for (size_t n = count; n > 0; n--) | |
| *--tmpdst = *--tmpsrc; | |
| } | |
| } | |
| #endif | |
| #if DCHAR_IS_TCHAR && !USE_SNPRINTF | |
| /* Make room for the result. */ | |
| if (count > allocated - length) | |
| { | |
| /* Need at least count elements. But allocate | |
| proportionally. */ | |
| size_t n = | |
| xmax (xsum (length, count), xtimes (allocated, 2)); | |
| ENSURE_ALLOCATION (n); | |
| } | |
| #endif | |
| /* Here count <= allocated - length. */ | |
| /* Perform padding. */ | |
| #if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT | |
| if (pad_ourselves && has_width) | |
| { | |
| size_t w; | |
| # if ENABLE_UNISTDIO | |
| /* Outside POSIX, it's preferable to compare the width | |
| against the number of _characters_ of the converted | |
| value. */ | |
| w = DCHAR_MBSNLEN (result + length, count); | |
| # elif __GLIBC__ >= 2 | |
| /* glibc prefers to compare the width against the number | |
| of characters as well, but only for numeric conversion | |
| specifiers. See | |
| <https://sourceware.org/PR28943> | |
| <https://sourceware.org/PR30883> | |
| <https://sourceware.org/PR31542> */ | |
| switch (dp->conversion) | |
| { | |
| case 'd': case 'i': case 'u': | |
| case 'f': case 'F': case 'g': case 'G': | |
| w = DCHAR_MBSNLEN (result + length, count); | |
| break; | |
| default: | |
| w = count; | |
| break; | |
| } | |
| # else | |
| /* The width is compared against the number of _bytes_ | |
| of the converted value, says POSIX. */ | |
| w = count; | |
| # endif | |
| if (w < width) | |
| { | |
| size_t pad = width - w; | |
| /* Make room for the result. */ | |
| if (xsum (count, pad) > allocated - length) | |
| { | |
| /* Need at least count + pad elements. But | |
| allocate proportionally. */ | |
| size_t n = | |
| xmax (xsum3 (length, count, pad), | |
| xtimes (allocated, 2)); | |
| # if USE_SNPRINTF | |
| length += count; | |
| ENSURE_ALLOCATION (n); | |
| length -= count; | |
| # else | |
| ENSURE_ALLOCATION (n); | |
| # endif | |
| } | |
| /* Here count + pad <= allocated - length. */ | |
| { | |
| # if !DCHAR_IS_TCHAR || USE_SNPRINTF | |
| DCHAR_T * const rp = result + length; | |
| # else | |
| DCHAR_T * const rp = tmp; | |
| # endif | |
| DCHAR_T *p = rp + count; | |
| DCHAR_T *end = p + pad; | |
| DCHAR_T *pad_ptr; | |
| # if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO | |
| if (dp->conversion == 'c' | |
| || dp->conversion == 's') | |
| /* No zero-padding for string directives. */ | |
| pad_ptr = NULL; | |
| else | |
| # endif | |
| { | |
| pad_ptr = (*rp == '-' ? rp + 1 : rp); | |
| /* No zero-padding of "inf" and "nan". */ | |
| if ((*pad_ptr >= 'A' && *pad_ptr <= 'Z') | |
| || (*pad_ptr >= 'a' && *pad_ptr <= 'z')) | |
| pad_ptr = NULL; | |
| else | |
| /* Do the zero-padding after the "0x" or | |
| "0b" prefix, not before. */ | |
| if (p - rp >= 2 | |
| && *rp == '0' | |
| && (((dp->conversion == 'a' | |
| || dp->conversion == 'x') | |
| && rp[1] == 'x') | |
| || ((dp->conversion == 'A' | |
| || dp->conversion == 'X') | |
| && rp[1] == 'X') | |
| || (dp->conversion == 'b' | |
| && rp[1] == 'b') | |
| || (dp->conversion == 'B' | |
| && rp[1] == 'B'))) | |
| pad_ptr += 2; | |
| } | |
| /* The generated string now extends from rp to p, | |
| with the zero padding insertion point being at | |
| pad_ptr. */ | |
| count = count + pad; /* = end - rp */ | |
| if (flags & FLAG_LEFT) | |
| { | |
| /* Pad with spaces on the right. */ | |
| for (; pad > 0; pad--) | |
| *p++ = ' '; | |
| } | |
| else if ((flags & FLAG_ZERO) && pad_ptr != NULL | |
| /* ISO C says: "For d, i, o, u, x, and X | |
| conversions, if a precision is | |
| specified, the 0 flag is ignored. */ | |
| && !(has_precision | |
| && (dp->conversion == 'd' | |
| || dp->conversion == 'i' | |
| || dp->conversion == 'o' | |
| || dp->conversion == 'u' | |
| || dp->conversion == 'x' | |
| || dp->conversion == 'X' | |
| /* Although ISO C does not | |
| require it, treat 'b' and 'B' | |
| like 'x' and 'X'. */ | |
| || dp->conversion == 'b' | |
| || dp->conversion == 'B'))) | |
| { | |
| /* Pad with zeroes. */ | |
| DCHAR_T *q = end; | |
| while (p > pad_ptr) | |
| *--q = *--p; | |
| for (; pad > 0; pad--) | |
| *p++ = '0'; | |
| } | |
| else | |
| { | |
| /* Pad with spaces on the left. */ | |
| DCHAR_T *q = end; | |
| while (p > rp) | |
| *--q = *--p; | |
| for (; pad > 0; pad--) | |
| *p++ = ' '; | |
| } | |
| } | |
| } | |
| } | |
| #endif | |
| /* Here still count <= allocated - length. */ | |
| #if !DCHAR_IS_TCHAR || USE_SNPRINTF | |
| /* The snprintf() result did fit. */ | |
| #else | |
| /* Append the sprintf() result. */ | |
| memcpy (result + length, tmp, count * sizeof (DCHAR_T)); | |
| #endif | |
| #if !USE_SNPRINTF | |
| if (tmp != tmpbuf) | |
| free (tmp); | |
| #endif | |
| #if NEED_PRINTF_DIRECTIVE_F | |
| if (dp->conversion == 'F') | |
| { | |
| /* Convert the %f result to upper case for %F. */ | |
| DCHAR_T *rp = result + length; | |
| for (size_t rc = count; rc > 0; rc--, rp++) | |
| if (*rp >= 'a' && *rp <= 'z') | |
| *rp = *rp - 'a' + 'A'; | |
| } | |
| #endif | |
| length += count; | |
| break; | |
| } | |
| errno = orig_errno; | |
| #undef pad_ourselves | |
| #undef prec_ourselves | |
| } | |
| } | |
| } | |
| /* Add the final NUL. */ | |
| ENSURE_ALLOCATION (xsum (length, 1)); | |
| result[length] = '\0'; | |
| if (result != resultbuf && length + 1 < allocated) | |
| { | |
| /* Shrink the allocated memory if possible. */ | |
| DCHAR_T *memory = | |
| (DCHAR_T *) realloc (result, (length + 1) * sizeof (DCHAR_T)); | |
| if (memory != NULL) | |
| result = memory; | |
| } | |
| if (buf_malloced != NULL) | |
| free (buf_malloced); | |
| CLEANUP (); | |
| *lengthp = length; | |
| /* Note that we can produce a big string of a length > INT_MAX. POSIX | |
| says that snprintf() fails with errno = EOVERFLOW in this case, but | |
| that's only because snprintf() returns an 'int'. This function does | |
| not have this limitation. */ | |
| return result; | |
| overflow: | |
| errno = EOVERFLOW; | |
| goto fail_with_errno; | |
| out_of_memory: | |
| errno = ENOMEM; | |
| goto fail_with_errno; | |
| #if ENABLE_UNISTDIO || (WIDE_CHAR_VERSION || !USE_SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION) || (NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION) | |
| fail_with_EILSEQ: | |
| errno = EILSEQ; | |
| goto fail_with_errno; | |
| #endif | |
| fail_with_errno: | |
| if (result != resultbuf) | |
| free (result); | |
| if (buf_malloced != NULL) | |
| free (buf_malloced); | |
| CLEANUP (); | |
| return NULL; | |
| } | |
| out_of_memory_1: | |
| errno = ENOMEM; | |
| goto fail_1_with_errno; | |
| fail_1_with_EINVAL: | |
| errno = EINVAL; | |
| goto fail_1_with_errno; | |
| fail_1_with_errno: | |
| CLEANUP (); | |
| return NULL; | |
| } | |
| #undef MAX_ROOM_NEEDED | |
| #undef TCHARS_PER_DCHAR | |
| #undef SNPRINTF | |
| #undef USE_SNPRINTF | |
| #undef DCHAR_SET | |
| #undef DCHAR_CPY | |
| #undef PRINTF_PARSE | |
| #undef DIRECTIVES | |
| #undef DIRECTIVE | |
| #undef DCHAR_IS_TCHAR | |
| #undef TCHAR_T | |
| #undef DCHAR_T | |
| #undef FCHAR_T | |
| #undef VASNPRINTF |