From: Bruno Haible <bruno@clisp.org>
To: bug-gnulib@gnu.org
Subject: vasnwprintf: Port to older platforms without swprintf
Date: Mon, 20 Mar 2023 19:29:05 +0100 [thread overview]
Message-ID: <15428020.lVVuGzaMjS@nimes> (raw)
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 <bruno@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
next reply other threads:[~2023-03-20 18:29 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-03-20 18:29 Bruno Haible [this message]
2023-03-21 16:52 ` vasnwprintf: Port to older platforms without swprintf Bruno Haible
2023-03-22 13:45 ` Bruno Haible
2023-03-22 18:42 ` Bruno Haible
2023-03-22 20:28 ` Bruno Haible
2023-03-22 20:34 ` Bruno Haible
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://lists.gnu.org/mailman/listinfo/bug-gnulib
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=15428020.lVVuGzaMjS@nimes \
--to=bruno@clisp.org \
--cc=bug-gnulib@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).