diff options
Diffstat (limited to 'macros/texinfo/texinfo/gnulib/lib/vasnprintf.c')
-rw-r--r-- | macros/texinfo/texinfo/gnulib/lib/vasnprintf.c | 423 |
1 files changed, 332 insertions, 91 deletions
diff --git a/macros/texinfo/texinfo/gnulib/lib/vasnprintf.c b/macros/texinfo/texinfo/gnulib/lib/vasnprintf.c index b3854e3ef9..f9124fa716 100644 --- a/macros/texinfo/texinfo/gnulib/lib/vasnprintf.c +++ b/macros/texinfo/texinfo/gnulib/lib/vasnprintf.c @@ -1,5 +1,5 @@ /* vsprintf with automatic memory allocation. - Copyright (C) 1999, 2002-2019 Free Software Foundation, Inc. + Copyright (C) 1999, 2002-2021 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,7 +41,14 @@ 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. */ + 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 @@ -87,6 +94,7 @@ /* Checked size_t computations. */ #include "xsize.h" +#include "attribute.h" #include "verify.h" #if (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL @@ -118,14 +126,6 @@ # include "fpucw.h" #endif -#ifndef FALLTHROUGH -# if __GNUC__ < 7 -# define FALLTHROUGH ((void) 0) -# else -# define FALLTHROUGH __attribute__ ((__fallthrough__)) -# endif -#endif - /* Default parameters. */ #ifndef VASNPRINTF # if WIDE_CHAR_VERSION @@ -277,6 +277,74 @@ local_wcsnlen (const wchar_t *s, size_t maxlen) # endif #endif +#if (((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL) || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || (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 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) && !defined IN_LIBINTL /* Determine the decimal-point character according to the current locale. */ # ifndef decimal_point_char_defined @@ -565,7 +633,8 @@ divide (mpn_t a, mpn_t b, mpn_t *q) 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) +# 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 @@ -1553,16 +1622,13 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, switch (conversion) { case 'd': case 'i': case 'u': -# if HAVE_LONG_LONG_INT if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.30103 /* binary -> decimal */ ) + 1; /* turn floor into ceil */ - else -# endif - if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + else if (type == TYPE_LONGINT || type == TYPE_ULONGINT) tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.30103 /* binary -> decimal */ @@ -1583,16 +1649,13 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, break; case 'o': -# if HAVE_LONG_LONG_INT if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.333334 /* binary -> octal */ ) + 1; /* turn floor into ceil */ - else -# endif - if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + else if (type == TYPE_LONGINT || type == TYPE_ULONGINT) tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.333334 /* binary -> octal */ @@ -1611,16 +1674,13 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, break; case 'x': case 'X': -# if HAVE_LONG_LONG_INT if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ ) + 1; /* turn floor into ceil */ - else -# endif - if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + else if (type == TYPE_LONGINT || type == TYPE_ULONGINT) tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ @@ -1686,7 +1746,13 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, case 'c': # if HAVE_WINT_T && !WIDE_CHAR_VERSION if (type == TYPE_WIDE_CHAR) - tmp_length = MB_CUR_MAX; + { + 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; @@ -1939,11 +2005,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, case TYPE_COUNT_LONGINT_POINTER: *a.arg[dp->arg_index].a.a_count_longint_pointer = length; break; -#if HAVE_LONG_LONG_INT case TYPE_COUNT_LONGLONGINT_POINTER: *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; break; -#endif default: abort (); } @@ -2405,7 +2469,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } } #endif -#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL)) && HAVE_WCHAR_T +#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL) || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T else if (dp->conversion == 's' # if WIDE_CHAR_VERSION && a.arg[dp->arg_index].type != TYPE_WIDE_STRING @@ -2680,11 +2744,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (*arg_end == 0) /* Found the terminating null wide character. */ break; -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - count = wcrtomb (cbuf, *arg_end, &state); -# else - count = wctomb (cbuf, *arg_end); -# endif + count = local_wcrtomb (cbuf, *arg_end, &state); if (count < 0) { /* Cannot convert. */ @@ -2725,11 +2785,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (*arg_end == 0) /* Found the terminating null wide character. */ break; -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - count = wcrtomb (cbuf, *arg_end, &state); -# else - count = wctomb (cbuf, *arg_end); -# endif + count = local_wcrtomb (cbuf, *arg_end, &state); if (count < 0) { /* Cannot convert. */ @@ -2774,11 +2830,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (*arg == 0) abort (); -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - count = wcrtomb (cbuf, *arg, &state); -# else - count = wctomb (cbuf, *arg); -# endif + count = local_wcrtomb (cbuf, *arg, &state); if (count <= 0) /* Inconsistency. */ abort (); @@ -2855,11 +2907,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (*arg == 0) abort (); -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - count = wcrtomb (cbuf, *arg, &state); -# else - count = wctomb (cbuf, *arg); -# endif + count = local_wcrtomb (cbuf, *arg, &state); if (count <= 0) /* Inconsistency. */ abort (); @@ -2884,11 +2932,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (*arg == 0) abort (); -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - count = wcrtomb (cbuf, *arg, &state); -# else - count = wctomb (cbuf, *arg); -# endif + count = local_wcrtomb (cbuf, *arg, &state); if (count <= 0) { /* Cannot convert. */ @@ -2924,6 +2968,210 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # endif } #endif +#if 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 + the fallback that avoids EILSEQ. */ + int flags = dp->flags; + int has_width; + size_t width; + + has_width = 0; + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + 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); + } + 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 characters; +# if !DCHAR_IS_TCHAR + /* This code assumes that TCHAR_T is 'char'. */ + verify (sizeof (TCHAR_T) == 1); + TCHAR_T tmpsrc[64]; /* Assume MB_CUR_MAX <= 64. */ + DCHAR_T *tmpdst; + size_t tmpdst_len; +# endif + size_t w; + +# if DCHAR_IS_TCHAR + if (has_width) +# endif + { + /* Count the number of bytes. */ + characters = 0; + if (arg != 0) + { + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count; +# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t + mbstate_t state; + memset (&state, '\0', sizeof (mbstate_t)); +# endif + + count = local_wcrtomb (cbuf, arg, &state); + if (count < 0) + /* Inconsistency. */ + abort (); + characters = count; + } + } +# if DCHAR_IS_TCHAR + else + { + /* The number of bytes doesn't matter. */ + characters = 0; + } +# endif + +# if !DCHAR_IS_TCHAR + /* Convert the string into a piece of temporary memory. */ + if (characters > 0) /* implies arg != 0 */ + { + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count; +# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t + mbstate_t state; + memset (&state, '\0', sizeof (mbstate_t)); +# endif + + 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, characters, + NULL, + NULL, &tmpdst_len); + if (tmpdst == NULL) + { + int saved_errno = errno; + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = saved_errno; + return NULL; + } +# 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. */ + w = DCHAR_MBSNLEN (result + length, characters); +# else + /* The width is compared against the number of _bytes_ + of the converted value, says POSIX. */ + w = characters; +# endif + } + else + /* w doesn't matter. */ + w = 0; + + if (w < width && !(dp->flags & FLAG_LEFT)) + { + size_t n = width - w; + ENSURE_ALLOCATION (xsum (length, n)); + 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, characters)); + if (characters > 0) /* implies arg != 0 */ + { + int count; +# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t + mbstate_t state; + memset (&state, '\0', sizeof (mbstate_t)); +# endif + + count = local_wcrtomb (result + length, arg, &state); + if (count <= 0) + /* Inconsistency. */ + abort (); + length += count; + } + } + else + { + if (arg != 0) + { + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count; +# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t + mbstate_t state; + memset (&state, '\0', sizeof (mbstate_t)); +# endif + + count = local_wcrtomb (cbuf, arg, &state); + if (count <= 0) + /* Inconsistency. */ + abort (); + ENSURE_ALLOCATION (xsum (length, count)); + memcpy (result + length, cbuf, count); + length += count; + } + } +# else + ENSURE_ALLOCATION (xsum (length, tmpdst_len)); + DCHAR_CPY (result + length, tmpdst, tmpdst_len); + free (tmpdst); + length += tmpdst_len; +# endif + + if (w < width && (dp->flags & FLAG_LEFT)) + { + size_t n = width - w; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + } +#endif #if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL else if ((dp->conversion == 'a' || dp->conversion == 'A') # if !(NEED_PRINTF_DIRECTIVE_A || (NEED_PRINTF_LONG_DOUBLE && NEED_PRINTF_DOUBLE)) @@ -4835,17 +5083,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, switch (type) { -#if HAVE_LONG_LONG_INT case TYPE_LONGLONGINT: case TYPE_ULONGLONGINT: -# if defined _WIN32 && ! defined __CYGWIN__ +#if defined _WIN32 && ! defined __CYGWIN__ *fbp++ = 'I'; *fbp++ = '6'; *fbp++ = '4'; break; -# else +#else *fbp++ = 'l'; -# endif #endif FALLTHROUGH; case TYPE_LONGINT: @@ -4871,39 +5117,32 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #endif *fbp = dp->conversion; #if USE_SNPRINTF -# if ! (((__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) \ - && !defined __UCLIBC__) \ - || (defined __APPLE__ && defined __MACH__) \ - || defined __ANDROID__ \ - || (defined _WIN32 && ! defined __CYGWIN__)) - fbp[1] = '%'; - fbp[2] = 'n'; - fbp[3] = '\0'; -# else - /* On glibc2 systems from glibc >= 2.3 - probably also older - ones - we know that snprintf's return value conforms to - ISO C 99: the tests gl_SNPRINTF_RETVAL_C99 and - gl_SNPRINTF_TRUNCATION_C99 pass. - Therefore we can avoid using %n in this situation. - 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), so we should avoid it - in this situation. */ - /* On Mac OS X 10.3 or newer, we know that snprintf's return - value conforms to ISO C 99: the tests gl_SNPRINTF_RETVAL_C99 - and gl_SNPRINTF_TRUNCATION_C99 pass. - Therefore we can avoid using %n in this situation. - On Mac OS X 10.13 or newer, the use of %n in format strings - in writable memory by default crashes the program, so we - should avoid it in this situation. */ - /* On Android, we know that snprintf's return value conforms to - ISO C 99: the tests gl_SNPRINTF_RETVAL_C99 and - gl_SNPRINTF_TRUNCATION_C99 pass. - Therefore we can avoid using %n in this situation. - Starting on 2018-03-07, the use of %n in format strings - produces a fatal error (see - <https://android.googlesource.com/platform/bionic/+/41398d03b7e8e0dfb951660ae713e682e9fc0336>), - so we should avoid it. */ +# if ((HAVE_SNPRINTF_RETVAL_C99 && HAVE_SNPRINTF_TRUNCATION_C99) \ + || ((__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) \ + && !defined __UCLIBC__) \ + || (defined __APPLE__ && defined __MACH__) \ + || defined __ANDROID__ \ + || (defined _WIN32 && ! defined __CYGWIN__)) + /* On systems 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), it is possible to avoid + using %n. And it is desirable to do so, because more and + more platforms no longer support %n, for "security reasons". + In particular, the following platforms: + - 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 Mac OS X 10.13 or newer, the use of %n in format + strings in writable memory by default crashes the + program. + - 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 these platforms, HAVE_SNPRINTF_RETVAL_C99 and + HAVE_SNPRINTF_TRUNCATION_C99 are 1. We have listed them + explicitly in the condition above, in case of cross- + compilation (just to be sure). */ /* On native Windows systems (such as mingw), we can avoid using %n because: - Although the gl_SNPRINTF_TRUNCATION_C99 test fails, @@ -4920,6 +5159,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/set-printf-count-output> So we should avoid %n in this situation. */ fbp[1] = '\0'; +# else /* AIX <= 5.1, HP-UX, IRIX, OSF/1, Solaris <= 9, BeOS */ + fbp[1] = '%'; + fbp[2] = 'n'; + fbp[3] = '\0'; # endif #else fbp[1] = '\0'; @@ -5063,7 +5306,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, SNPRINTF_BUF (arg); } break; -#if HAVE_LONG_LONG_INT case TYPE_LONGLONGINT: { long long int arg = a.arg[dp->arg_index].a.a_longlongint; @@ -5076,7 +5318,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, SNPRINTF_BUF (arg); } break; -#endif case TYPE_DOUBLE: { double arg = a.arg[dp->arg_index].a.a_double; |