On platforms without swprintf, such as IRIX 6.5, I was seeing this warning, and later a link error:
In file included from ../../gllib/vasnwprintf.c:18: ../../gllib/vasnprintf.c: In function `vasnwprintf': ../../gllib/vasnprintf.c:3509: warning: implicit declaration of function `swprintf' This patch fixes it, thus making vasnwprintf work on older platforms such as NetBSD 3.0, OpenBSD 3.8, HP-UX 11.00, IRIX 6.5. 2023-03-20 Bruno Haible <br...@clisp.org> vasnwprintf: Port to older platforms without swprintf. * m4/vasnprintf.m4 (gl_PREREQ_VASNWPRINTF): Test for swprintf. * lib/vasnprintf.c (TCHAR_T, DCHAR_IS_TCHAR, SNPRINTF): When WIDE_CHAR_VERSION and swprintf does not exist, use TCHAR_T = char, SNPRINTF = snprintf, and !DCHAR_IS_TCHAR. (VASNPRINTF): In this case, implement %ls and %lc directly. Adjust a couple of #if conditions. For the conversion from TCHAR_T[] to DCHAR_T[], use mbsrtowcs. * modules/vasnwprintf (Depends-on): Add mbsrtowcs. diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 50ff2e64dd..2150bead4d 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -138,8 +138,6 @@ # define VASNPRINTF vasnwprintf # define FCHAR_T wchar_t # define DCHAR_T wchar_t -# define TCHAR_T wchar_t -# define DCHAR_IS_TCHAR 1 # define DIRECTIVE wchar_t_directive # define DIRECTIVES wchar_t_directives # define PRINTF_PARSE wprintf_parse @@ -159,24 +157,32 @@ # endif #endif #if WIDE_CHAR_VERSION - /* TCHAR_T is wchar_t. */ -# 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 + /* DCHAR_T is wchar_t. */ +# if HAVE_DECL__SNWPRINTF || HAVE_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 -# define SNPRINTF _snwprintf -# define USE_MSVC__SNPRINTF 1 + /* Unix. */ +# define SNPRINTF swprintf # endif # else - /* Unix. */ -# define SNPRINTF swprintf + /* Old platforms such as NetBSD 3.0, OpenBSD 3.8, HP-UX 11.00, IRIX 6.5. */ +# define TCHAR_T char # endif -#else +#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 @@ -2413,6 +2419,151 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } } #endif +#if WIDE_CHAR_VERSION && !DCHAR_IS_TCHAR + 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; + int has_precision; + size_t precision; + + 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_precision = 0; + precision = 6; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + 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 wchar_t *arg; + wchar_t lc_arg[1]; + size_t characters; + + if (dp->conversion == 's') + { + 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 *arg_end; + + arg_end = arg; + characters = 0; + for (; precision > 0; precision--) + { + if (*arg_end == 0) + /* Found the terminating null wide character. */ + break; + arg_end++; + characters++; + } + } + else + { + /* Use the entire string, and count the number of wide + characters. */ + characters = local_wcslen (arg); + } + } + else /* dp->conversion == 'c' */ + { + lc_arg[0] = (wchar_t) a.arg[dp->arg_index].a.a_wide_char; + arg = lc_arg; + if (has_precision && precision == 0) + characters = 0; + else + 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, arg, characters); + length += characters; + } + + if (characters < width && (flags & FLAG_LEFT)) + { + size_t n = width - characters; + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + } + } +#endif #if (!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T else if (dp->conversion == 's' # if WIDE_CHAR_VERSION @@ -3502,7 +3653,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } } *p++ = dp->conversion - 'A' + 'P'; -# if WIDE_CHAR_VERSION +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR { static const wchar_t decimal_format[] = { '%', '+', 'd', '\0' }; @@ -3653,7 +3804,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } } *p++ = dp->conversion - 'A' + 'P'; -# if WIDE_CHAR_VERSION +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR { static const wchar_t decimal_format[] = { '%', '+', 'd', '\0' }; @@ -5203,7 +5354,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #if USE_SNPRINTF /* Decide whether to pass %n in the format string to SNPRINTF. */ -# if ((!WIDE_CHAR_VERSION \ +# if (((!WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR) \ && (HAVE_SNPRINTF_RETVAL_C99 && HAVE_SNPRINTF_TRUNCATION_C99)) \ || ((__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) \ && !defined __UCLIBC__) \ @@ -5214,9 +5365,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, || (WIDE_CHAR_VERSION && MUSL_LIBC) /* We can avoid passing %n and instead rely on SNPRINTF's return value if - - !WIDE_CHAR_VERSION, because if WIDE_CHAR_VERSION, + - !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 + 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 @@ -5509,13 +5662,14 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* Look at the snprintf() return value. */ if (retcount < 0) { -# if WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF +# 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, the + 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 @@ -5698,6 +5852,23 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # else tmpsrc = tmp; # endif +# if WIDE_CHAR_VERSION + const TCHAR_T *tmpsrc2; + mbstate_t state; + + tmpsrc2 = tmpsrc; + memset (&state, '\0', sizeof (mbstate_t)); + tmpdst_len = mbsrtowcs (NULL, &tmpsrc2, 0, &state); + if (tmpdst_len == (size_t) -1) + goto fail_with_errno; + tmpdst = + (wchar_t *) malloc ((tmpdst_len + 1) * sizeof (wchar_t)); + if (tmpdst == NULL) + goto out_of_memory; + tmpsrc2 = tmpsrc; + memset (&state, '\0', sizeof (mbstate_t)); + (void) mbsrtowcs (tmpdst, &tmpsrc2, tmpdst_len + 1, &state); +# else tmpdst = DCHAR_CONV_FROM_ENCODING (locale_charset (), iconveh_question_mark, @@ -5706,6 +5877,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, 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); diff --git a/m4/vasnprintf.m4 b/m4/vasnprintf.m4 index 8e582dd791..f807e4771f 100644 --- a/m4/vasnprintf.m4 +++ b/m4/vasnprintf.m4 @@ -1,4 +1,4 @@ -# vasnprintf.m4 serial 42 +# vasnprintf.m4 serial 43 dnl Copyright (C) 2002-2004, 2006-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -94,7 +94,7 @@ AC_DEFUN_ONCE([gl_PREREQ_VASNPRINTF] # Prerequisites of lib/vasnwprintf.c. AC_DEFUN_ONCE([gl_PREREQ_VASNWPRINTF], [ - AC_CHECK_FUNCS([wcsnlen mbrtowc]) + AC_CHECK_FUNCS([swprintf wcsnlen mbrtowc]) AC_CHECK_DECLS([_snwprintf], , , [[#include <stdio.h>]]) gl_MUSL_LIBC gl_PREREQ_VASNXPRINTF diff --git a/modules/vasnwprintf b/modules/vasnwprintf index 07a078696c..109c39f1c5 100644 --- a/modules/vasnwprintf +++ b/modules/vasnwprintf @@ -35,6 +35,7 @@ errno memchr assert-h wchar +mbsrtowcs wmemcpy wmemset