bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

c32*: Optionally enforce ISO C 23 semantics of char32_t


From: Bruno Haible
Subject: c32*: Optionally enforce ISO C 23 semantics of char32_t
Date: Tue, 27 Jun 2023 14:53:22 +0200

The ISO C 11 semantics of char32_t is implementation dependent.
The ISO C 23 semantics of char32_t is that it's Unicode code points.

So far, Gnulib implements the ISO C 11 semantics only. But as soon as an
application want to use libunistring functions or do other Unicode-based
processing, it needs the ISO C 23 semantics.

This patch introduces a module 'uchar-c23' that achieves this. It does so
by converting between the wchar_t and Unicode representations in the
various *c32* functions, on the relevant platforms (macOS, FreeBSD, NetBSD,
Solaris), through iconv(). So:
  - The upside of this module is that you get Unicode code points.
  - The downside of this module is that it's slower on some non-GNU platforms,
    and the binaries require linking with -liconv. You should use
    the contents of the variables $(LIBUNISTRING) $(LIBC32CONV).


2023-06-27  Bruno Haible  <bruno@clisp.org>

        c32*: Optionally enforce ISO C 23 semantics of char32_t.
        * lib/lc-charset-unicode.h: New file.
        * lib/lc-charset-unicode.c: New file.
        * modules/uchar-c23: New file.
        * lib/uchar.in.h (char32_t): Add comment.
        * lib/mbrtoc32.c: Include lc-charset-unicode.h.
        (mbrtoc32): If char32_t is Unicode and wchar_t is not, invoke
        locale_encoding_to_unicode.
        * lib/btoc32.c: Include lc-charset-unicode.h.
        (btoc32): If char32_t is Unicode and wchar_t is not, invoke
        locale_encoding_to_unicode.
        * lib/c32rtomb.c: Include lc-charset-unicode.h.
        (c32rtomb): If char32_t is Unicode and wchar_t is not, invoke
        unicode_to_locale_encoding.
        * lib/c32tob.c: Include lc-charset-unicode.h.
        (c32tob): If char32_t is Unicode and wchar_t is not, invoke
        unicode_to_locale_encoding.
        * lib/mbsnrtoc32s.c: If char32_t is Unicode and wchar_t is not, don't
        use mbsnrtowcs.
        * lib/mbsrtoc32s.c: If char32_t is Unicode and wchar_t is not, don't use
        mbsrtowcs.
        * lib/c32snrtombs.c: If char32_t is Unicode and wchar_t is not, don't
        use wcsnrtombs.
        * lib/c32srtombs.c: If char32_t is Unicode and wchar_t is not, don't use
        wcsrtombs.
        * lib/c32is-impl.h: Include lc-charset-unicode.h.
        (FUNC): If char32_t is Unicode and wchar_t is not, use UCS_FUNC.
        * lib/c32to-impl.h: Include lc-charset-unicode.h.
        (FUNC): If char32_t is Unicode and wchar_t is not, use UCS_FUNC.
        * lib/c32width.c: Include lc-charset-unicode.h.
        (c32width): If char32_t is Unicode and wchar_t is not, use uc_width.
        * tests/test-mbrtoc32.c: Include <wchar.h>.
        (main): Skip GB18030 tests on NetBSD and Solaris. If
        GL_CHAR32_T_IS_UNICODE, expect Unicode encoding for the char32_t values.
        * tests/test-mbrtoc32-w32.c: Include <wchar.h>.
        (test_one_locale): Simplify.
        * tests/test-c32rtomb.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32rtomb.sh: Update.
        * tests/test-mbsnrtoc32s.c: Include <wchar.h>.
        (main): Skip GB18030 tests on NetBSD and Solaris.
        * tests/test-mbsrtoc32s.c: Include <wchar.h>.
        (main): Skip GB18030 tests on NetBSD and Solaris.
        * tests/test-mbstoc32s.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32snrtombs.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32srtombs.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32stombs.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32isalnum.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32isalnum.sh: Update.
        * tests/test-c32isalpha.c (main): Skip GB18030 tests on NetBSD and
        Solaris. Disable tests that would fail on macOS, FreeBSD, NetBSD,
        Solaris.
        * tests/test-c32isalpha.sh: Update.
        * tests/test-c32isblank.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32isblank.sh: Update.
        * tests/test-c32iscntrl.c (main): Skip GB18030 tests on NetBSD and
        Solaris. Disable tests that would fail on macOS, FreeBSD, NetBSD,
        Solaris.
        * tests/test-c32iscntrl.sh: Update.
        * tests/test-c32isdigit.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32isdigit.sh: Update.
        * tests/test-c32isgraph.c (main): Skip GB18030 tests on NetBSD and
        Solaris. Disable tests that would fail on macOS, FreeBSD, NetBSD,
        Solaris.
        * tests/test-c32isgraph.sh: Update.
        * tests/test-c32islower.c (main): Skip GB18030 tests on NetBSD and
        Solaris. Disable tests that would fail on macOS, FreeBSD, NetBSD,
        Solaris.
        * tests/test-c32islower.sh: Update.
        * tests/test-c32isprint.c (main): Skip GB18030 tests on NetBSD and
        Solaris. Disable tests that would fail on macOS, FreeBSD, NetBSD,
        Solaris.
        * tests/test-c32isprint.sh: Update.
        * tests/test-c32ispunct.c (main): Skip GB18030 tests on NetBSD and
        Solaris. Disable tests that would fail on macOS, FreeBSD, NetBSD,
        Solaris.
        * tests/test-c32ispunct.sh: Update.
        * tests/test-c32isspace.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32isspace.sh: Update.
        * tests/test-c32isupper.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32isupper.sh: Update.
        * tests/test-c32isxdigit.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32isxdigit.sh: Update.
        * tests/test-c32tolower.c (main): Skip GB18030 tests on NetBSD and
        Solaris.
        * tests/test-c32tolower.sh: Update.
        * tests/test-c32toupper.c (main): Skip GB18030 tests on NetBSD and
        Solaris. Disable tests that would fail on macOS, FreeBSD, NetBSD,
        Solaris.
        * tests/test-c32toupper.sh: Update.
        * modules/mbrtoc32 (Link): Add $(LIBUNISTRING) $(LIBC32CONV).
        * modules/mbrtoc32-tests (Makefile.am): Link test-mbrtoc32 with
        $(LIBUNISTRING) $(LIBC32CONV).
        * modules/btoc32 (Link): New section.
        * modules/btoc32-tests (Makefile.am): Link test-btoc32 with
        $(LIBUNISTRING) $(LIBC32CONV).
        * modules/c32rtomb (Link): New section.
        * modules/c32rtomb-tests (Makefile.am): Link test-c32rtomb with
        $(LIBUNISTRING) $(LIBC32CONV).
        * modules/c32tob (Link): New section.
        * modules/mbsnrtoc32s (Link): Add $(LIBUNISTRING) $(LIBC32CONV).
        * modules/mbsnrtoc32s-tests (Makefile.am): Link test-mbsnrtoc32s with
        $(LIBUNISTRING) $(LIBC32CONV).
        * modules/mbsrtoc32s (Link): Add $(LIBUNISTRING) $(LIBC32CONV).
        * modules/mbsrtoc32s-tests (Makefile.am): Link test-mbsrtoc32s with
        $(LIBUNISTRING) $(LIBC32CONV).
        * modules/mbstoc32s (Link): Add $(LIBUNISTRING) $(LIBC32CONV).
        * modules/mbstoc32s-tests (Makefile.am): Link test-mbstoc32s with
        $(LIBUNISTRING) $(LIBC32CONV).
        * modules/c32snrtombs (Link): New section.
        * modules/c32snrtombs-tests (Makefile.am): Link test-c32snrtombs with
        $(LIBUNISTRING) $(LIBC32CONV).
        * modules/c32srtombs (Link): New section.
        * modules/c32srtombs-tests (Makefile.am): Link test-c32srtombs with
        $(LIBUNISTRING) $(LIBC32CONV).
        * modules/c32stombs (Link): New section.
        * modules/c32stombs-tests (Makefile.am): Link test-c32stombs with
        $(LIBUNISTRING) $(LIBC32CONV).
        * modules/c32isalnum (Link): Add $(LIBC32CONV).
        * modules/c32isalnum-tests (Makefile.am): Link test-c32isalnum with
        $(LIBC32CONV).
        * modules/c32isalpha (Link): Add $(LIBC32CONV).
        * modules/c32isalpha-tests (Makefile.am): Link test-c32isalpha with
        $(LIBC32CONV).
        * modules/c32isblank (Link): Add $(LIBC32CONV).
        * modules/c32isblank-tests (Makefile.am): Link test-c32isblank with
        $(LIBC32CONV).
        * modules/c32iscntrl (Link): Add $(LIBC32CONV).
        * modules/c32iscntrl-tests (Makefile.am): Link test-c32iscntrl with
        $(LIBC32CONV).
        * modules/c32isdigit (Link): Add $(LIBC32CONV).
        * modules/c32isdigit-tests (Makefile.am): Link test-c32isdigit with
        $(LIBC32CONV).
        * modules/c32isgraph (Link): Add $(LIBC32CONV).
        * modules/c32isgraph-tests (Makefile.am): Link test-c32isgraph with
        $(LIBC32CONV).
        * modules/c32islower (Link): Add $(LIBC32CONV).
        * modules/c32islower-tests (Makefile.am): Link test-c32islower with
        $(LIBC32CONV).
        * modules/c32isprint (Link): Add $(LIBC32CONV).
        * modules/c32isprint-tests (Makefile.am): Link test-c32isprint with
        $(LIBC32CONV).
        * modules/c32ispunct (Link): Add $(LIBC32CONV).
        * modules/c32ispunct-tests (Makefile.am): Link test-c32ispunct with
        $(LIBC32CONV).
        * modules/c32isspace (Link): Add $(LIBC32CONV).
        * modules/c32isspace-tests (Makefile.am): Link test-c32isspace with
        $(LIBC32CONV).
        * modules/c32isupper (Link): Add $(LIBC32CONV).
        * modules/c32isupper-tests (Makefile.am): Link test-c32isupper with
        $(LIBC32CONV).
        * modules/c32isxdigit (Link): Add $(LIBC32CONV).
        * modules/c32isxdigit-tests (Makefile.am): Link test-c32isxdigit with
        $(LIBC32CONV).
        * modules/c32tolower (Link): Add $(LIBC32CONV).
        * modules/c32tolower-tests (Makefile.am): Link test-c32tolower with
        $(LIBC32CONV).
        * modules/c32toupper (Link): Add $(LIBC32CONV).
        * modules/c32toupper-tests (Makefile.am): Link test-c32toupper with
        $(LIBC32CONV).
        * modules/c32width (Link): Add $(LIBC32CONV).
        * modules/c32width-tests (Makefile.am): Link test-c32width with
        $(LIBC32CONV).
        * modules/c32swidth (Link): Add $(LIBC32CONV).
        * modules/c32swidth-tests (Makefile.am): Link test-c32swidth with
        $(LIBC32CONV).
        * modules/mbchar (Link): Add $(LIBC32CONV).
        * modules/mbiter (Link): Add $(LIBC32CONV).
        * modules/mbuiter (Link): Add $(LIBC32CONV).
        * modules/mbfile (Link): Add $(LIBC32CONV).
        * modules/mbmemcasecmp (Link): Add $(LIBC32CONV).
        * modules/mbmemcasecmp-tests (Makefile.am): Link test-mbmemcasecmp with
        $(LIBC32CONV).
        * modules/mbscasecmp (Link): Add $(LIBC32CONV).
        * modules/mbscasecmp-tests (Makefile.am): Link test-mbscasecmp with
        $(LIBC32CONV).
        * modules/mbscasestr (Link): Add $(LIBC32CONV).
        * modules/mbscasestr-tests (Makefile.am): Link test-mbscasestr1,
        test-mbscasestr2, test-mbscasestr3, test-mbscasestr4 with $(LIBC32CONV).
        * modules/mbschr (Link): Add $(LIBC32CONV).
        * modules/mbschr-tests (Makefile.am): Link test-mbschr with
        $(LIBC32CONV).
        * modules/mbscspn (Link): Add $(LIBC32CONV).
        * modules/mbscspn-tests (Makefile.am): Link test-mbscspn with
        $(LIBC32CONV).
        * modules/mbslen (Link): Add $(LIBC32CONV).
        * modules/mbsncasecmp (Link): Add $(LIBC32CONV).
        * modules/mbsncasecmp-tests (Makefile.am): Link test-mbsncasecmp with
        $(LIBC32CONV).
        * modules/mbsnlen (Link): Add $(LIBC32CONV).
        * modules/mbspbrk (Link): Add $(LIBC32CONV).
        * modules/mbspbrk-tests (Makefile.am): Link test-mbspbrk with
        $(LIBC32CONV).
        * modules/mbspcasecmp (Link): Add $(LIBC32CONV).
        * modules/mbspcasecmp-tests (Makefile.am): Link test-mbspcasecmp with
        $(LIBC32CONV).
        * modules/mbsrchr (Link): Add $(LIBC32CONV).
        * modules/mbsrchr-tests (Makefile.am): Link test-mbsrchr with
        $(LIBC32CONV).
        * modules/mbssep (Link): Add $(LIBC32CONV).
        * modules/mbsspn (Link): Add $(LIBC32CONV).
        * modules/mbsspn-tests (Makefile.am): Link test-mbsspn with
        $(LIBC32CONV).
        * modules/mbsstr (Link): Add $(LIBC32CONV).
        * modules/mbsstr-tests (Makefile.am): Link test-mbsstr1, test-mbsstr2,
        test-mbsstr3 with $(LIBC32CONV).
        * modules/mbstok_r (Link): Add $(LIBC32CONV).
        * modules/propername (Link): Add $(LIBC32CONV).
        * modules/regex-quote (Link): Add $(LIBC32CONV).
        * modules/regex-quote-tests (Makefile.am): Link test-regex-quote with
        $(LIBC32CONV).
        * modules/trim (Link): Add $(LIBC32CONV).
        * modules/trim-tests (Makefile.am): Link test-trim with $(LIBC32CONV).
        * modules/exclude (Link): Add $(LIBC32CONV).
        * modules/exclude-tests (Makefile.am): Link test-exclude with
        $(LIBC32CONV).
        * doc/posix-headers/uchar.texi: Mention the uchar-c23 module.
        * doc/posix-functions/mbrtoc32.texi: Likewise.
        * doc/strings.texi (The char32_t type): Likewise.

diff --git a/doc/posix-functions/mbrtoc32.texi 
b/doc/posix-functions/mbrtoc32.texi
index 27700d4376..91f88bf86f 100644
--- a/doc/posix-functions/mbrtoc32.texi
+++ b/doc/posix-functions/mbrtoc32.texi
@@ -31,3 +31,7 @@
 This function is only defined as an inline function on some platforms:
 Haiku 2020.
 @end itemize
+
+Note: If you want the guarantee that the @code{char32_t} values returned
+by this function are Unicode code points, you also need to request the
+@code{uchar-c23} module.
diff --git a/doc/posix-headers/uchar.texi b/doc/posix-headers/uchar.texi
index 64c8e3a8d7..60e717ff21 100644
--- a/doc/posix-headers/uchar.texi
+++ b/doc/posix-headers/uchar.texi
@@ -5,9 +5,9 @@
 functions @code{mbrtoc16}, @code{c16rtomb}, @code{mbrtoc32},
 @code{c32rtomb}.
 
-Gnulib module: uchar
+Gnulib module: uchar or uchar-c23
 
-Portability problems fixed by Gnulib:
+Portability problems fixed by either Gnulib module @code{uchar} or 
@code{uchar-c23}:
 @itemize
 @item
 This header file is missing on many non-glibc platforms:
@@ -21,6 +21,14 @@
 AIX 7.2 with xlclang++.
 @end itemize
 
+Portability problems fixed by Gnulib module @code{uchar-c23}:
+@itemize
+@item
+@code{char32_t} values may not be Unicode code points.
+This is the case in ISO C 11 compliant but not ISO C 23 compliant
+implementations.
+@end itemize
+
 Portability problems not fixed by Gnulib:
 @itemize
 @end itemize
diff --git a/doc/strings.texi b/doc/strings.texi
index 7a074e4040..ecf92f3da4 100644
--- a/doc/strings.texi
+++ b/doc/strings.texi
@@ -703,6 +703,15 @@
 @code{*c32*} functions are optimized so that on glibc systems they
 immediately redirect to the corresponding @code{*wc*} functions.
 
+Gnulib implements the ISO C 23 semantics of @code{char32_t} when you
+import the @samp{uchar-c23} module.  Without this module, it implements
+only the ISO C 11 semantics; the effect is that on some platforms
+(macOS, FreeBSD, NetBSD, Solaris) a @code{char32_t} value is the same
+as a @code{wchar_t} value, not a Unicode code point.  Thus, when you
+want to pass @code{char32_t} values to GNU libunistring or to some Unicode
+centric Gnulib functions, you need the @samp{uchar-c23} module in order
+to do so without portability problems.
+
 @node The mbchar_t type
 @subsection The @code{mbchar_t} type
 
diff --git a/lib/btoc32.c b/lib/btoc32.c
index 07fbe194b6..c5ed227a8b 100644
--- a/lib/btoc32.c
+++ b/lib/btoc32.c
@@ -25,6 +25,10 @@
 #include <stdio.h>
 #include <string.h>
 
+#if GL_CHAR32_T_IS_UNICODE
+# include "lc-charset-unicode.h"
+#endif
+
 #if _GL_WCHAR_T_IS_UCS4
 _GL_EXTERN_INLINE
 #endif
@@ -49,6 +53,15 @@ btoc32 (int c)
 #else
   /* In all known locale encodings, unibyte characters correspond only to
      characters in the BMP.  */
-  return btowc (c);
+  wint_t wc = btowc (c);
+# if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
+  if (wc != WEOF && wc != 0)
+    {
+      wc = locale_encoding_to_unicode (wc);
+      if (wc == 0)
+        return WEOF;
+    }
+# endif
+  return wc;
 #endif
 }
diff --git a/lib/c32is-impl.h b/lib/c32is-impl.h
index 74278417b1..aadc1bc7bd 100644
--- a/lib/c32is-impl.h
+++ b/lib/c32is-impl.h
@@ -28,6 +28,10 @@
 # include "streq.h"
 #endif
 
+#if GL_CHAR32_T_IS_UNICODE
+# include "lc-charset-unicode.h"
+#endif
+
 #include "unictype.h"
 
 #if _GL_WCHAR_T_IS_UCS4 && !GNULIB_defined_mbstate_t
@@ -92,6 +96,10 @@ FUNC (wint_t wc)
   /* char32_t and wchar_t are equivalent.  */
   static_assert (sizeof (char32_t) == sizeof (wchar_t));
 
+# if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
+  return UCS_FUNC (wc);
+# else
   return WCHAR_FUNC (wc);
+# endif
 #endif
 }
diff --git a/lib/c32rtomb.c b/lib/c32rtomb.c
index 298c6be235..1f6acc97d4 100644
--- a/lib/c32rtomb.c
+++ b/lib/c32rtomb.c
@@ -28,6 +28,10 @@
 #include "localcharset.h"
 #include "streq.h"
 
+#if GL_CHAR32_T_IS_UNICODE
+# include "lc-charset-unicode.h"
+#endif
+
 size_t
 c32rtomb (char *s, char32_t wc, mbstate_t *ps)
 #undef c32rtomb
@@ -111,6 +115,17 @@ c32rtomb (char *s, char32_t wc, mbstate_t *ps)
 #else
 
   /* char32_t and wchar_t are equivalent.  */
+# if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
+  if (wc != 0)
+    {
+      wc = unicode_to_locale_encoding (wc);
+      if (wc == 0)
+        {
+          errno = EILSEQ;
+          return (size_t)(-1);
+        }
+    }
+# endif
   return wcrtomb (s, (wchar_t) wc, ps);
 
 #endif
diff --git a/lib/c32snrtombs.c b/lib/c32snrtombs.c
index e47df6bf8e..1e0bee42f1 100644
--- a/lib/c32snrtombs.c
+++ b/lib/c32snrtombs.c
@@ -24,7 +24,7 @@
 
 #include <wchar.h>
 
-#if (HAVE_WORKING_MBRTOC32 && !_GL_WCHAR_T_IS_UCS4) || _GL_SMALL_WCHAR_T
+#if (HAVE_WORKING_MBRTOC32 && !_GL_WCHAR_T_IS_UCS4) || (GL_CHAR32_T_IS_UNICODE 
&& GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION) || _GL_SMALL_WCHAR_T
 /* The char32_t encoding of a multibyte character may be different than its
    wchar_t encoding, or char32_t is wider than wchar_t.  */
 
diff --git a/lib/c32srtombs.c b/lib/c32srtombs.c
index 306c75ae43..cc3796100f 100644
--- a/lib/c32srtombs.c
+++ b/lib/c32srtombs.c
@@ -24,7 +24,7 @@
 
 #include <wchar.h>
 
-#if (HAVE_WORKING_MBRTOC32 && !_GL_WCHAR_T_IS_UCS4) || _GL_SMALL_WCHAR_T
+#if (HAVE_WORKING_MBRTOC32 && !_GL_WCHAR_T_IS_UCS4) || (GL_CHAR32_T_IS_UNICODE 
&& GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION) || _GL_SMALL_WCHAR_T
 /* The char32_t encoding of a multibyte character may be different than its
    wchar_t encoding, or char32_t is wider than wchar_t.  */
 
diff --git a/lib/c32to-impl.h b/lib/c32to-impl.h
index 923825589b..e3e6ad1631 100644
--- a/lib/c32to-impl.h
+++ b/lib/c32to-impl.h
@@ -24,6 +24,10 @@
 # include "streq.h"
 #endif
 
+#if GL_CHAR32_T_IS_UNICODE
+# include "lc-charset-unicode.h"
+#endif
+
 #include "unicase.h"
 
 #if _GL_WCHAR_T_IS_UCS4 && !GNULIB_defined_mbstate_t
@@ -79,6 +83,10 @@ FUNC (wint_t wc)
   /* char32_t and wchar_t are equivalent.  */
   static_assert (sizeof (char32_t) == sizeof (wchar_t));
 
+# if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
+  return UCS_FUNC (wc);
+# else
   return WCHAR_FUNC (wc);
+# endif
 #endif
 }
diff --git a/lib/c32tob.c b/lib/c32tob.c
index 7b164352c5..f0e0c35ef9 100644
--- a/lib/c32tob.c
+++ b/lib/c32tob.c
@@ -26,6 +26,10 @@
 #include <string.h>
 #include <wchar.h>
 
+#if GL_CHAR32_T_IS_UNICODE
+# include "lc-charset-unicode.h"
+#endif
+
 #if _GL_WCHAR_T_IS_UCS4
 _GL_EXTERN_INLINE
 #endif
@@ -53,6 +57,14 @@ c32tob (wint_t wc)
   else
     return EOF;
 #else
+# if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
+  if (wc != 0)
+    {
+      wc = unicode_to_locale_encoding (wc);
+      if (wc == 0)
+        return EOF;
+    }
+# endif
   return wctob (wc);
 #endif
 }
diff --git a/lib/c32width.c b/lib/c32width.c
index 76371ac741..16ed595278 100644
--- a/lib/c32width.c
+++ b/lib/c32width.c
@@ -33,6 +33,11 @@
 #endif
 
 #include "localcharset.h"
+
+#if GL_CHAR32_T_IS_UNICODE
+# include "lc-charset-unicode.h"
+#endif
+
 #include "uniwidth.h"
 
 #if _GL_WCHAR_T_IS_UCS4 && !GNULIB_defined_mbstate_t
@@ -89,6 +94,9 @@ c32width (char32_t wc)
   /* char32_t and wchar_t are equivalent.  */
   static_assert (sizeof (char32_t) == sizeof (wchar_t));
 
+# if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
+  return uc_width (wc, locale_charset ());
+# endif
   return wcwidth (wc);
 #endif
 }
diff --git a/lib/lc-charset-unicode.c b/lib/lc-charset-unicode.c
new file mode 100644
index 0000000000..afbc188ad7
--- /dev/null
+++ b/lib/lc-charset-unicode.c
@@ -0,0 +1,266 @@
+/* Conversion between the current locale's character encoding and Unicode.
+   Copyright (C) 2023 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/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "lc-charset-unicode.h"
+
+#if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
+
+/* We use iconv() to convert between a 'wchar_t' value and a Unicode code 
point.
+   For performance reasons, we don't allocate an iconv_t for each conversion,
+   but instead cache it for subsequent conversions.  Since an iconv_t 
descriptor
+   can only be used in a single thread at a time, this cache must be
+   per-thread.  */
+
+# include <iconv.h>
+# include <stdlib.h>
+# include <string.h>
+# include <wchar.h>
+
+# include "localcharset.h"
+# include "streq.h"
+# include "glthread/lock.h"
+# include "glthread/tls.h"
+# include "unistr.h"
+
+/* Maximum length of encoding.  Attained for "ISO-8859-15".  */
+# define MAX_ENCODING_LEN 11
+
+struct converters
+{
+  iconv_t cd_locale_to_utf8;
+  iconv_t cd_utf8_to_locale;
+  /* NUL-terminated encoding name.  */
+  char encoding[MAX_ENCODING_LEN + 1];
+};
+
+static gl_tls_key_t converters_key; /* TLS key for a 'struct converters *' */
+
+/* Frees a 'struct converters *', for example when a thread terminates.  */
+static void
+free_converters (void *p)
+{
+  if (p != NULL)
+    {
+      struct converters *conv = p;
+      iconv_close (conv->cd_locale_to_utf8);
+      iconv_close (conv->cd_utf8_to_locale);
+      free (conv);
+    }
+}
+
+static void
+key_init (void)
+{
+  gl_tls_key_init (converters_key, free_converters);
+  /* The per-thread initial value is NULL.  */
+}
+
+/* Ensure that key_init is called once only.  */
+gl_once_define(static, key_init_once)
+
+/* Returns the per-thread 'struct converters *' that contains converters for 
the
+   given encoding.  Returns NULL upon failure.  */
+static struct converters *
+get_converters (const char *encoding)
+{
+  if (strlen (encoding) > MAX_ENCODING_LEN)
+    /* If this happens, increase MAX_ENCODING_LEN.  */
+    return NULL;
+
+  gl_once (key_init_once, key_init);
+  struct converters *conv = gl_tls_get (converters_key);
+  if (conv == NULL)
+    {
+      conv = (struct converters *) malloc (sizeof (struct converters));
+      if (conv == NULL)
+        /* Out of memory.  */
+        return NULL;
+      conv->cd_locale_to_utf8 = iconv_open ("UTF-8", encoding);
+      conv->cd_utf8_to_locale = iconv_open (encoding, "UTF-8");
+      if (conv->cd_locale_to_utf8 == (iconv_t)(-1)
+          || conv->cd_utf8_to_locale == (iconv_t)(-1))
+        {
+          /* iconv does not support this encoding.  */
+          if (conv->cd_locale_to_utf8 != (iconv_t)(-1))
+            iconv_close (conv->cd_locale_to_utf8);
+          if (conv->cd_utf8_to_locale != (iconv_t)(-1))
+            iconv_close (conv->cd_utf8_to_locale);
+          free (conv);
+          return NULL;
+        }
+      strcpy (conv->encoding, encoding);
+    }
+  else if (strcmp (conv->encoding, encoding) != 0)
+    {
+      /* The locale encoding of this thread changed.  */
+      iconv_t new_cd_locale_to_utf8 = iconv_open ("UTF-8", encoding);
+      iconv_t new_cd_utf8_to_locale = iconv_open (encoding, "UTF-8");
+      if (new_cd_locale_to_utf8 == (iconv_t)(-1)
+          || new_cd_utf8_to_locale == (iconv_t)(-1))
+        {
+          /* iconv does not support this encoding.  */
+          if (new_cd_locale_to_utf8 != (iconv_t)(-1))
+            iconv_close (new_cd_locale_to_utf8);
+          if (new_cd_utf8_to_locale != (iconv_t)(-1))
+            iconv_close (new_cd_utf8_to_locale);
+          return NULL;
+        }
+      iconv_close (conv->cd_locale_to_utf8);
+      iconv_close (conv->cd_utf8_to_locale);
+      conv->cd_locale_to_utf8 = new_cd_locale_to_utf8;
+      conv->cd_utf8_to_locale = new_cd_utf8_to_locale;
+      strcpy (conv->encoding, encoding);
+    }
+  return conv;
+}
+
+char32_t
+locale_encoding_to_unicode (wchar_t wc)
+{
+  /* This function is like a simplified variant of u32_conv_from_encoding,
+     that uses a cached per-thread iconv_t instead of allocating an iconv_t
+     at each call.  */
+  if (wc == 0)
+    /* Invalid argument.  */
+    abort ();
+
+  const char *encoding = locale_charset ();
+  if (STREQ_OPT (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0))
+    /* Assume that if the locale encoding is UTF-8, the wchar_t encoding is
+       Unicode.  */
+    return wc;
+  if (STREQ_OPT (encoding, "ASCII", 'A', 'S', 'C', 'I', 'I', 0, 0, 0, 0))
+    /* In the POSIX locale, avoid conversion errors.  */
+    return wc;
+
+  struct converters *conv = get_converters (encoding);
+  if (conv == NULL)
+    return 0;
+
+  char mbbuf[64];
+  size_t mbcnt;
+  {
+    mbstate_t state = { 0 };
+    mbcnt = wcrtomb (mbbuf, wc, &state);
+    if (mbcnt > sizeof (mbbuf))
+      /* wcrtomb did not recognize the wide character wc.  */
+      abort ();
+  }
+
+  char utf8buf[6];
+  size_t utf8cnt;
+  {
+    char *mbptr = mbbuf;
+    size_t mbsize = mbcnt;
+    char *utf8ptr = utf8buf;
+    size_t utf8size = sizeof (utf8buf);
+    size_t ret = iconv (conv->cd_locale_to_utf8,
+                        &mbptr, &mbsize,
+                        &utf8ptr, &utf8size);
+    if (ret == (size_t)(-1))
+      /* Conversion error.  */
+      return 0;
+    if (mbsize != 0)
+      /* The input was not entirely converted.  */
+      return 0;
+    utf8cnt = sizeof (utf8buf) - utf8size; /* = utf8ptr - utf8buf */
+    if (utf8cnt == 0)
+      /* The conversion produced no output.  */
+      return 0;
+  }
+
+  ucs4_t uc;
+  if (u8_mbtouc (&uc, (const uint8_t *) utf8buf, utf8cnt) != utf8cnt)
+    /* iconv produced an invalid UTF-8 byte sequence.  */
+    abort ();
+
+  return uc;
+}
+
+wchar_t
+unicode_to_locale_encoding (char32_t uc)
+{
+  if (uc == 0)
+    /* Invalid argument.  */
+    abort ();
+
+  /* This function is like a simplified variant of u32_conv_to_encoding
+     that uses a cached per-thread iconv_t instead of allocating an iconv_t
+     at each call.  */
+  const char *encoding = locale_charset ();
+  if (STREQ_OPT (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0))
+    /* Assume that if the locale encoding is UTF-8, the wchar_t encoding is
+       Unicode.  */
+    return uc;
+  if (STREQ_OPT (encoding, "ASCII", 'A', 'S', 'C', 'I', 'I', 0, 0, 0, 0))
+    /* In the POSIX locale, avoid conversion errors.  */
+    return uc;
+
+  struct converters *conv = get_converters (encoding);
+  if (conv == NULL)
+    return 0;
+
+  char utf8buf[6];
+  int utf8cnt = u8_uctomb ((uint8_t *) utf8buf, uc, sizeof (utf8buf));
+  if (utf8cnt < 0)
+    /* Out-of-range Unicode character.  */
+    return 0;
+
+  char mbbuf[64];
+  size_t mbcnt;
+  {
+    char *utf8ptr = utf8buf;
+    size_t utf8size = utf8cnt;
+    char *mbptr = mbbuf;
+    size_t mbsize = sizeof (mbbuf);
+    size_t ret = iconv (conv->cd_utf8_to_locale,
+                        &utf8ptr, &utf8size,
+                        &mbptr, &mbsize);
+    if (ret == (size_t)(-1))
+      /* Conversion error.  */
+      return 0;
+    if (utf8size != 0)
+      /* The input was not entirely converted.  */
+      return 0;
+    mbcnt = sizeof (mbbuf) - mbsize; /* = mbptr - mbbuf */
+    if (mbcnt == 0)
+      /* The conversion produced no output.  */
+      return 0;
+  }
+
+  wchar_t wc;
+  {
+    mbstate_t state = { 0 };
+    if (mbrtowc (&wc, mbbuf, mbcnt, &state) != mbcnt)
+      /* iconv produced an invalid multibyte sequence.  */
+      return 0;
+  }
+
+  return wc;
+}
+
+#else
+
+/* This declaration is solely to ensure that after preprocessing
+   this file is never empty.  */
+typedef int dummy;
+
+#endif
diff --git a/lib/lc-charset-unicode.h b/lib/lc-charset-unicode.h
new file mode 100644
index 0000000000..f5a9ba9d37
--- /dev/null
+++ b/lib/lc-charset-unicode.h
@@ -0,0 +1,33 @@
+/* Conversion between the current locale's character encoding and Unicode.
+   Copyright (C) 2023 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/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
+
+/* This facility is only needed on specific platforms.  */
+#if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
+
+# include <wchar.h>
+# include <uchar.h>
+
+/* Returns WC (must be != 0) as a Unicode character,
+   or 0 in case it cannot be converted.  */
+extern char32_t locale_encoding_to_unicode (wchar_t wc);
+
+/* Returns UC (must be != 0) as a wide character,
+   or 0 in case it cannot be converted.  */
+extern wchar_t unicode_to_locale_encoding (char32_t uc);
+
+#endif
diff --git a/lib/mbrtoc32.c b/lib/mbrtoc32.c
index 2dca9e1e50..6a56d93a4b 100644
--- a/lib/mbrtoc32.c
+++ b/lib/mbrtoc32.c
@@ -26,6 +26,10 @@
 #include <errno.h>
 #include <stdlib.h>
 
+#if GL_CHAR32_T_IS_UNICODE
+# include "lc-charset-unicode.h"
+#endif
+
 #if GNULIB_defined_mbstate_t /* AIX, IRIX */
 /* Implement mbrtoc32() on top of mbtowc() for the non-UTF-8 locales
    and directly for the UTF-8 locales.  */
@@ -242,6 +246,17 @@ mbrtoc32 (char32_t *pwc, const char *s, size_t n, 
mbstate_t *ps)
   /* char32_t and wchar_t are equivalent.  Use mbrtowc().  */
   wchar_t wc;
   size_t ret = mbrtowc (&wc, s, n, ps);
+#  if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
+  if (ret < (size_t) -2 && wc != 0)
+    {
+      wc = locale_encoding_to_unicode (wc);
+      if (wc == 0)
+        {
+          ret = (size_t) -1;
+          errno = EILSEQ;
+        }
+    }
+#  endif
   if (ret < (size_t) -2 && pwc != NULL)
     *pwc = wc;
   return ret;
diff --git a/lib/mbsnrtoc32s.c b/lib/mbsnrtoc32s.c
index d52e9934e0..5baa08fd4e 100644
--- a/lib/mbsnrtoc32s.c
+++ b/lib/mbsnrtoc32s.c
@@ -23,7 +23,7 @@
 
 #include <wchar.h>
 
-#if (HAVE_WORKING_MBRTOC32 && !_GL_WCHAR_T_IS_UCS4) || _GL_SMALL_WCHAR_T
+#if (HAVE_WORKING_MBRTOC32 && !_GL_WCHAR_T_IS_UCS4) || (GL_CHAR32_T_IS_UNICODE 
&& GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION) || _GL_SMALL_WCHAR_T
 /* The char32_t encoding of a multibyte character may be different than its
    wchar_t encoding, or char32_t is wider than wchar_t.  */
 
diff --git a/lib/mbsrtoc32s.c b/lib/mbsrtoc32s.c
index 32306a50e7..d37e67056b 100644
--- a/lib/mbsrtoc32s.c
+++ b/lib/mbsrtoc32s.c
@@ -23,7 +23,7 @@
 
 #include <wchar.h>
 
-#if (HAVE_WORKING_MBRTOC32 && !_GL_WCHAR_T_IS_UCS4) || _GL_SMALL_WCHAR_T
+#if (HAVE_WORKING_MBRTOC32 && !_GL_WCHAR_T_IS_UCS4) || (GL_CHAR32_T_IS_UNICODE 
&& GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION) || _GL_SMALL_WCHAR_T
 /* The char32_t encoding of a multibyte character may be different than its
    wchar_t encoding, or char32_t is wider than wchar_t.  */
 
diff --git a/lib/uchar.in.h b/lib/uchar.in.h
index f46551d910..acf730c8ca 100644
--- a/lib/uchar.in.h
+++ b/lib/uchar.in.h
@@ -110,7 +110,9 @@ typedef uint_least16_t gl_char16_t;
 /* A 32-bit variant of wchar_t.
    Note: This type is only mandated by ISO C 11 or newer.  In ISO C 23
    and newer, it denotes UTF-32 code points; in older versions of ISO C
-   it did so only on platforms on which __STDC_UTF_32__ was defined.  */
+   it did so only on platforms on which __STDC_UTF_32__ was defined.
+   In gnulib, we guarantee that it denotes UTF-32 code points if and
+   only if the module 'uchar-c23' is in use.  */
 typedef uint_least32_t char32_t;
 
 #elif @GNULIBHEADERS_OVERRIDE_CHAR32_T@
diff --git a/modules/btoc32 b/modules/btoc32
index caf36d346b..1a532a9eb3 100644
--- a/modules/btoc32
+++ b/modules/btoc32
@@ -18,6 +18,10 @@ lib_SOURCES += btoc32.c
 Include:
 <uchar.h>
 
+Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
+
 License:
 LGPLv2+
 
diff --git a/modules/btoc32-tests b/modules/btoc32-tests
index b9d14eefd1..a5ff796d3e 100644
--- a/modules/btoc32-tests
+++ b/modules/btoc32-tests
@@ -19,4 +19,4 @@ Makefile.am:
 TESTS += test-btoc32-1.sh test-btoc32-2.sh test-btoc32-3.sh
 TESTS_ENVIRONMENT += LOCALE_FR='@LOCALE_FR@' LOCALE_FR_UTF8='@LOCALE_FR_UTF8@'
 check_PROGRAMS += test-btoc32
-test_btoc32_LDADD = $(LDADD) $(SETLOCALE_LIB)
+test_btoc32_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(LIBC32CONV)
diff --git a/modules/c32isalnum b/modules/c32isalnum
index f980602218..f202a37cf0 100644
--- a/modules/c32isalnum
+++ b/modules/c32isalnum
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32isalnum-tests b/modules/c32isalnum-tests
index 3c9036cb96..3c8c52a5a9 100644
--- a/modules/c32isalnum-tests
+++ b/modules/c32isalnum-tests
@@ -26,4 +26,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32isalnum
-test_c32isalnum_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32isalnum_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32isalpha b/modules/c32isalpha
index ed1e216307..1fc1ee58de 100644
--- a/modules/c32isalpha
+++ b/modules/c32isalpha
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32isalpha-tests b/modules/c32isalpha-tests
index 2748fee325..22efbd6842 100644
--- a/modules/c32isalpha-tests
+++ b/modules/c32isalpha-tests
@@ -28,4 +28,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32isalpha
-test_c32isalpha_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32isalpha_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32isblank b/modules/c32isblank
index 531cc6e806..ad1e7c139f 100644
--- a/modules/c32isblank
+++ b/modules/c32isblank
@@ -37,6 +37,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32isblank-tests b/modules/c32isblank-tests
index 9f19076407..ffcab15a9a 100644
--- a/modules/c32isblank-tests
+++ b/modules/c32isblank-tests
@@ -26,4 +26,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32isblank
-test_c32isblank_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32isblank_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32iscntrl b/modules/c32iscntrl
index f84937850a..2d423795e9 100644
--- a/modules/c32iscntrl
+++ b/modules/c32iscntrl
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32iscntrl-tests b/modules/c32iscntrl-tests
index 98a0cb8452..9a25ba9360 100644
--- a/modules/c32iscntrl-tests
+++ b/modules/c32iscntrl-tests
@@ -28,4 +28,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32iscntrl
-test_c32iscntrl_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32iscntrl_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32isdigit b/modules/c32isdigit
index a05a5f1429..631f50f917 100644
--- a/modules/c32isdigit
+++ b/modules/c32isdigit
@@ -37,6 +37,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32isdigit-tests b/modules/c32isdigit-tests
index fe83e357ed..bbb70cd352 100644
--- a/modules/c32isdigit-tests
+++ b/modules/c32isdigit-tests
@@ -26,4 +26,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32isdigit
-test_c32isdigit_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32isdigit_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32isgraph b/modules/c32isgraph
index 62e6832883..48b5e7b10a 100644
--- a/modules/c32isgraph
+++ b/modules/c32isgraph
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32isgraph-tests b/modules/c32isgraph-tests
index 236d456323..af717306e0 100644
--- a/modules/c32isgraph-tests
+++ b/modules/c32isgraph-tests
@@ -28,4 +28,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32isgraph
-test_c32isgraph_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32isgraph_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32islower b/modules/c32islower
index b349f3524b..40563f6f0a 100644
--- a/modules/c32islower
+++ b/modules/c32islower
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32islower-tests b/modules/c32islower-tests
index 2f9aa1600d..e3dac1d2d9 100644
--- a/modules/c32islower-tests
+++ b/modules/c32islower-tests
@@ -28,4 +28,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32islower
-test_c32islower_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32islower_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32isprint b/modules/c32isprint
index 799ca51b93..1fc5d22a65 100644
--- a/modules/c32isprint
+++ b/modules/c32isprint
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32isprint-tests b/modules/c32isprint-tests
index 410d4544b3..f421f1b9d5 100644
--- a/modules/c32isprint-tests
+++ b/modules/c32isprint-tests
@@ -28,4 +28,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32isprint
-test_c32isprint_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32isprint_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32ispunct b/modules/c32ispunct
index 640c8c27a6..bb78c9ea6a 100644
--- a/modules/c32ispunct
+++ b/modules/c32ispunct
@@ -37,6 +37,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32ispunct-tests b/modules/c32ispunct-tests
index 7475e3bd76..15297210b7 100644
--- a/modules/c32ispunct-tests
+++ b/modules/c32ispunct-tests
@@ -26,4 +26,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32ispunct
-test_c32ispunct_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32ispunct_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32isspace b/modules/c32isspace
index c8ae10569e..1b519e42ce 100644
--- a/modules/c32isspace
+++ b/modules/c32isspace
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32isspace-tests b/modules/c32isspace-tests
index 51988a7a48..6df45cd997 100644
--- a/modules/c32isspace-tests
+++ b/modules/c32isspace-tests
@@ -26,4 +26,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32isspace
-test_c32isspace_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32isspace_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32isupper b/modules/c32isupper
index 0139fe8702..08838dcecf 100644
--- a/modules/c32isupper
+++ b/modules/c32isupper
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32isupper-tests b/modules/c32isupper-tests
index db5783c181..e3584c8bd5 100644
--- a/modules/c32isupper-tests
+++ b/modules/c32isupper-tests
@@ -26,4 +26,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32isupper
-test_c32isupper_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32isupper_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32isxdigit b/modules/c32isxdigit
index 40ce6bcd00..e145ce36ce 100644
--- a/modules/c32isxdigit
+++ b/modules/c32isxdigit
@@ -38,6 +38,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32isxdigit-tests b/modules/c32isxdigit-tests
index 3c986916b7..7d119dbfe1 100644
--- a/modules/c32isxdigit-tests
+++ b/modules/c32isxdigit-tests
@@ -26,4 +26,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32isxdigit
-test_c32isxdigit_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32isxdigit_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32rtomb b/modules/c32rtomb
index f343595004..1662333e8d 100644
--- a/modules/c32rtomb
+++ b/modules/c32rtomb
@@ -31,6 +31,10 @@ endif
 Include:
 <uchar.h>
 
+Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
+
 License:
 LGPLv2+
 
diff --git a/modules/c32rtomb-tests b/modules/c32rtomb-tests
index f1f83ae45c..9a1e0dcede 100644
--- a/modules/c32rtomb-tests
+++ b/modules/c32rtomb-tests
@@ -40,4 +40,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32rtomb test-c32rtomb-w32
-test_c32rtomb_LDADD = $(LDADD) $(SETLOCALE_LIB)
+test_c32rtomb_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(LIBC32CONV)
diff --git a/modules/c32snrtombs b/modules/c32snrtombs
index 6edf7174ed..e1bd41470d 100644
--- a/modules/c32snrtombs
+++ b/modules/c32snrtombs
@@ -24,6 +24,10 @@ lib_SOURCES += c32snrtombs.c
 Include:
 <uchar.h>
 
+Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
+
 License:
 LGPL
 
diff --git a/modules/c32snrtombs-tests b/modules/c32snrtombs-tests
index d6e578d07d..a201a83a7e 100644
--- a/modules/c32snrtombs-tests
+++ b/modules/c32snrtombs-tests
@@ -29,4 +29,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32snrtombs
-test_c32snrtombs_LDADD = $(LDADD) $(SETLOCALE_LIB)
+test_c32snrtombs_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(LIBC32CONV)
diff --git a/modules/c32srtombs b/modules/c32srtombs
index 4d82ab7bdc..ac1aaa7a0b 100644
--- a/modules/c32srtombs
+++ b/modules/c32srtombs
@@ -24,6 +24,10 @@ lib_SOURCES += c32srtombs.c
 Include:
 <uchar.h>
 
+Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
+
 License:
 LGPL
 
diff --git a/modules/c32srtombs-tests b/modules/c32srtombs-tests
index bb12d4140b..3d6678e934 100644
--- a/modules/c32srtombs-tests
+++ b/modules/c32srtombs-tests
@@ -29,4 +29,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32srtombs
-test_c32srtombs_LDADD = $(LDADD) $(SETLOCALE_LIB)
+test_c32srtombs_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(LIBC32CONV)
diff --git a/modules/c32stombs b/modules/c32stombs
index 274187778b..1067c433af 100644
--- a/modules/c32stombs
+++ b/modules/c32stombs
@@ -18,6 +18,10 @@ lib_SOURCES += c32stombs.c
 Include:
 <uchar.h>
 
+Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
+
 License:
 LGPL
 
diff --git a/modules/c32stombs-tests b/modules/c32stombs-tests
index 92389ba29e..63ac2074b4 100644
--- a/modules/c32stombs-tests
+++ b/modules/c32stombs-tests
@@ -29,4 +29,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32stombs
-test_c32stombs_LDADD = $(LDADD) $(SETLOCALE_LIB)
+test_c32stombs_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(LIBC32CONV)
diff --git a/modules/c32swidth b/modules/c32swidth
index df60f6b473..4861b548de 100644
--- a/modules/c32swidth
+++ b/modules/c32swidth
@@ -28,6 +28,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32swidth-tests b/modules/c32swidth-tests
index e591b9109f..4ab0b00279 100644
--- a/modules/c32swidth-tests
+++ b/modules/c32swidth-tests
@@ -12,4 +12,4 @@ configure.ac:
 Makefile.am:
 TESTS += test-c32swidth
 check_PROGRAMS += test-c32swidth
-test_c32swidth_LDADD = $(LDADD) $(SETLOCALE_LIB) $(LIBUNISTRING)
+test_c32swidth_LDADD = $(LDADD) $(SETLOCALE_LIB) $(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32tob b/modules/c32tob
index 655986d9d2..51b1d80359 100644
--- a/modules/c32tob
+++ b/modules/c32tob
@@ -23,6 +23,10 @@ lib_SOURCES += c32tob.c
 Include:
 <uchar.h>
 
+Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
+
 License:
 LGPL
 
diff --git a/modules/c32tolower b/modules/c32tolower
index 601b013442..25579f5455 100644
--- a/modules/c32tolower
+++ b/modules/c32tolower
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32tolower-tests b/modules/c32tolower-tests
index 1153eabadc..48f53e42cf 100644
--- a/modules/c32tolower-tests
+++ b/modules/c32tolower-tests
@@ -27,4 +27,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32tolower
-test_c32tolower_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32tolower_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32toupper b/modules/c32toupper
index 54c79a6b36..792df3a096 100644
--- a/modules/c32toupper
+++ b/modules/c32toupper
@@ -36,6 +36,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32toupper-tests b/modules/c32toupper-tests
index 3de9a9da0d..701acc1c2e 100644
--- a/modules/c32toupper-tests
+++ b/modules/c32toupper-tests
@@ -29,4 +29,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-c32toupper
-test_c32toupper_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING)
+test_c32toupper_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/c32width b/modules/c32width
index 22c38841a9..aeee6856e5 100644
--- a/modules/c32width
+++ b/modules/c32width
@@ -33,6 +33,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/c32width-tests b/modules/c32width-tests
index caa18b362d..84faadf447 100644
--- a/modules/c32width-tests
+++ b/modules/c32width-tests
@@ -13,4 +13,4 @@ configure.ac:
 Makefile.am:
 TESTS += test-c32width
 check_PROGRAMS += test-c32width
-test_c32width_LDADD = $(LDADD) $(SETLOCALE_LIB) $(LIBUNISTRING)
+test_c32width_LDADD = $(LDADD) $(SETLOCALE_LIB) $(LIBUNISTRING) $(LIBC32CONV)
diff --git a/modules/exclude b/modules/exclude
index 93bfdaf4cf..bef926307f 100644
--- a/modules/exclude
+++ b/modules/exclude
@@ -30,6 +30,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 GPL
diff --git a/modules/exclude-tests b/modules/exclude-tests
index 1c3b0ab673..1fc3401672 100644
--- a/modules/exclude-tests
+++ b/modules/exclude-tests
@@ -26,4 +26,4 @@ TESTS += \
  test-exclude8.sh
 
 check_PROGRAMS += test-exclude
-test_exclude_LDADD = $(LDADD) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB) 
$(LIBTHREAD)
+test_exclude_LDADD = $(LDADD) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB) 
$(LIBTHREAD) $(LIBC32CONV)
diff --git a/modules/mbchar b/modules/mbchar
index 51a1c8e1b9..7d4c8f6cec 100644
--- a/modules/mbchar
+++ b/modules/mbchar
@@ -38,6 +38,7 @@ Include:
 
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/mbfile b/modules/mbfile
index f7a0dc6338..c446e37e17 100644
--- a/modules/mbfile
+++ b/modules/mbfile
@@ -27,6 +27,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbiter b/modules/mbiter
index 082afd42f2..1d50d1148e 100644
--- a/modules/mbiter
+++ b/modules/mbiter
@@ -27,6 +27,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/mbmemcasecmp b/modules/mbmemcasecmp
index cb290adabb..52db8d34ee 100644
--- a/modules/mbmemcasecmp
+++ b/modules/mbmemcasecmp
@@ -19,6 +19,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbmemcasecmp-tests b/modules/mbmemcasecmp-tests
index bb2c4e6231..50e9d68366 100644
--- a/modules/mbmemcasecmp-tests
+++ b/modules/mbmemcasecmp-tests
@@ -25,4 +25,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' \
   LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
 check_PROGRAMS += test-mbmemcasecmp
-test_mbmemcasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB)
+test_mbmemcasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/modules/mbrtoc32 b/modules/mbrtoc32
index 6e74fd3756..061e8a7aec 100644
--- a/modules/mbrtoc32
+++ b/modules/mbrtoc32
@@ -52,7 +52,9 @@ Include:
 <uchar.h>
 
 Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/mbrtoc32-tests b/modules/mbrtoc32-tests
index 95cc4750bd..3408f0c8e9 100644
--- a/modules/mbrtoc32-tests
+++ b/modules/mbrtoc32-tests
@@ -46,4 +46,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-mbrtoc32 test-mbrtoc32-w32
-test_mbrtoc32_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbrtoc32_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBC32CONV)
diff --git a/modules/mbscasecmp b/modules/mbscasecmp
index b1f7c0d4a9..d0dcdf6d29 100644
--- a/modules/mbscasecmp
+++ b/modules/mbscasecmp
@@ -20,6 +20,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbscasecmp-tests b/modules/mbscasecmp-tests
index cd974fab20..61282af862 100644
--- a/modules/mbscasecmp-tests
+++ b/modules/mbscasecmp-tests
@@ -15,4 +15,4 @@ Makefile.am:
 TESTS += test-mbscasecmp.sh
 TESTS_ENVIRONMENT += LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
 check_PROGRAMS += test-mbscasecmp
-test_mbscasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB)
+test_mbscasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/modules/mbscasestr b/modules/mbscasestr
index 672cac8960..15e925d6e1 100644
--- a/modules/mbscasestr
+++ b/modules/mbscasestr
@@ -26,6 +26,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbscasestr-tests b/modules/mbscasestr-tests
index 40f0944dba..867d2153e4 100644
--- a/modules/mbscasestr-tests
+++ b/modules/mbscasestr-tests
@@ -24,7 +24,7 @@ Makefile.am:
 TESTS += test-mbscasestr1 test-mbscasestr2.sh test-mbscasestr3.sh 
test-mbscasestr4.sh
 TESTS_ENVIRONMENT += LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' 
LOCALE_TR_UTF8='@LOCALE_TR_UTF8@' LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-mbscasestr1 test-mbscasestr2 test-mbscasestr3 
test-mbscasestr4
-test_mbscasestr1_LDADD = $(LDADD) $(LIBUNISTRING) $(MBRTOWC_LIB)
-test_mbscasestr2_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB)
-test_mbscasestr3_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB)
-test_mbscasestr4_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB)
+test_mbscasestr1_LDADD = $(LDADD) $(LIBUNISTRING) $(MBRTOWC_LIB) $(LIBC32CONV)
+test_mbscasestr2_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
+test_mbscasestr3_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
+test_mbscasestr4_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/modules/mbschr b/modules/mbschr
index 1a68a3c93d..e3382e08b4 100644
--- a/modules/mbschr
+++ b/modules/mbschr
@@ -20,6 +20,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbschr-tests b/modules/mbschr-tests
index 5ba4550d70..ef26e73363 100644
--- a/modules/mbschr-tests
+++ b/modules/mbschr-tests
@@ -15,4 +15,4 @@ Makefile.am:
 TESTS += test-mbschr.sh
 TESTS_ENVIRONMENT += LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-mbschr
-test_mbschr_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbschr_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBC32CONV)
diff --git a/modules/mbscspn b/modules/mbscspn
index 4b2350c324..76f5748f12 100644
--- a/modules/mbscspn
+++ b/modules/mbscspn
@@ -22,6 +22,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbscspn-tests b/modules/mbscspn-tests
index 3763e1ce3e..4d8cb013ea 100644
--- a/modules/mbscspn-tests
+++ b/modules/mbscspn-tests
@@ -15,4 +15,4 @@ Makefile.am:
 TESTS += test-mbscspn.sh
 TESTS_ENVIRONMENT += LOCALE_FR_UTF8='@LOCALE_FR_UTF8@'
 check_PROGRAMS += test-mbscspn
-test_mbscspn_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbscspn_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBC32CONV)
diff --git a/modules/mbslen b/modules/mbslen
index a643fb8bcb..55993aa59c 100644
--- a/modules/mbslen
+++ b/modules/mbslen
@@ -22,6 +22,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbsncasecmp b/modules/mbsncasecmp
index 25b8624f1b..6f4300d3b7 100644
--- a/modules/mbsncasecmp
+++ b/modules/mbsncasecmp
@@ -20,6 +20,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbsncasecmp-tests b/modules/mbsncasecmp-tests
index 079fedb691..5ed84188ea 100644
--- a/modules/mbsncasecmp-tests
+++ b/modules/mbsncasecmp-tests
@@ -15,4 +15,4 @@ Makefile.am:
 TESTS += test-mbsncasecmp.sh
 TESTS_ENVIRONMENT += LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
 check_PROGRAMS += test-mbsncasecmp
-test_mbsncasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB)
+test_mbsncasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/modules/mbsnlen b/modules/mbsnlen
index 2dc0222cfe..307b6c34b2 100644
--- a/modules/mbsnlen
+++ b/modules/mbsnlen
@@ -20,6 +20,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/mbsnrtoc32s b/modules/mbsnrtoc32s
index 996e8a1d8f..4134968a94 100644
--- a/modules/mbsnrtoc32s
+++ b/modules/mbsnrtoc32s
@@ -27,7 +27,9 @@ Include:
 <uchar.h>
 
 Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbsnrtoc32s-tests b/modules/mbsnrtoc32s-tests
index c7af163045..bb02c10852 100644
--- a/modules/mbsnrtoc32s-tests
+++ b/modules/mbsnrtoc32s-tests
@@ -35,4 +35,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-mbsnrtoc32s
-test_mbsnrtoc32s_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbsnrtoc32s_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/modules/mbspbrk b/modules/mbspbrk
index 5570a07f1b..2841721312 100644
--- a/modules/mbspbrk
+++ b/modules/mbspbrk
@@ -22,6 +22,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbspbrk-tests b/modules/mbspbrk-tests
index 7fa86d9a9b..9ca44124cb 100644
--- a/modules/mbspbrk-tests
+++ b/modules/mbspbrk-tests
@@ -15,4 +15,4 @@ Makefile.am:
 TESTS += test-mbspbrk.sh
 TESTS_ENVIRONMENT += LOCALE_FR_UTF8='@LOCALE_FR_UTF8@'
 check_PROGRAMS += test-mbspbrk
-test_mbspbrk_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbspbrk_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBC32CONV)
diff --git a/modules/mbspcasecmp b/modules/mbspcasecmp
index 20b53c41a6..32f95024f8 100644
--- a/modules/mbspcasecmp
+++ b/modules/mbspcasecmp
@@ -20,6 +20,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbspcasecmp-tests b/modules/mbspcasecmp-tests
index a7d5ad6aef..e82a37eb23 100644
--- a/modules/mbspcasecmp-tests
+++ b/modules/mbspcasecmp-tests
@@ -15,4 +15,4 @@ Makefile.am:
 TESTS += test-mbspcasecmp.sh
 TESTS_ENVIRONMENT += LOCALE_TR_UTF8='@LOCALE_TR_UTF8@'
 check_PROGRAMS += test-mbspcasecmp
-test_mbspcasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB)
+test_mbspcasecmp_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/modules/mbsrchr b/modules/mbsrchr
index 9b660b8036..fcf5681864 100644
--- a/modules/mbsrchr
+++ b/modules/mbsrchr
@@ -20,6 +20,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbsrchr-tests b/modules/mbsrchr-tests
index 18589a3be6..dba1470789 100644
--- a/modules/mbsrchr-tests
+++ b/modules/mbsrchr-tests
@@ -15,4 +15,4 @@ Makefile.am:
 TESTS += test-mbsrchr.sh
 TESTS_ENVIRONMENT += LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-mbsrchr
-test_mbsrchr_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbsrchr_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBC32CONV)
diff --git a/modules/mbsrtoc32s b/modules/mbsrtoc32s
index 0fec6bb042..816ce046d3 100644
--- a/modules/mbsrtoc32s
+++ b/modules/mbsrtoc32s
@@ -26,7 +26,9 @@ Include:
 <uchar.h>
 
 Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/mbsrtoc32s-tests b/modules/mbsrtoc32s-tests
index 0adc62368e..0851867518 100644
--- a/modules/mbsrtoc32s-tests
+++ b/modules/mbsrtoc32s-tests
@@ -35,4 +35,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-mbsrtoc32s
-test_mbsrtoc32s_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbsrtoc32s_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/modules/mbssep b/modules/mbssep
index 0a93cf0c7e..a79538e21e 100644
--- a/modules/mbssep
+++ b/modules/mbssep
@@ -22,6 +22,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbsspn b/modules/mbsspn
index 650c1f4de4..5cc407299e 100644
--- a/modules/mbsspn
+++ b/modules/mbsspn
@@ -21,6 +21,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbsspn-tests b/modules/mbsspn-tests
index d2cc24c206..d720e47bd9 100644
--- a/modules/mbsspn-tests
+++ b/modules/mbsspn-tests
@@ -15,4 +15,4 @@ Makefile.am:
 TESTS += test-mbsspn.sh
 TESTS_ENVIRONMENT += LOCALE_FR_UTF8='@LOCALE_FR_UTF8@'
 check_PROGRAMS += test-mbsspn
-test_mbsspn_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbsspn_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBC32CONV)
diff --git a/modules/mbsstr b/modules/mbsstr
index c417f6bc3f..654760cfbe 100644
--- a/modules/mbsstr
+++ b/modules/mbsstr
@@ -25,6 +25,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbsstr-tests b/modules/mbsstr-tests
index 826ad32f83..34d6fa89b6 100644
--- a/modules/mbsstr-tests
+++ b/modules/mbsstr-tests
@@ -20,6 +20,6 @@ Makefile.am:
 TESTS += test-mbsstr1 test-mbsstr2.sh test-mbsstr3.sh
 TESTS_ENVIRONMENT += LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' 
LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-mbsstr1 test-mbsstr2 test-mbsstr3
-test_mbsstr1_LDADD = $(LDADD) $(LIBUNISTRING) $(MBRTOWC_LIB)
-test_mbsstr2_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
-test_mbsstr3_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbsstr1_LDADD = $(LDADD) $(LIBUNISTRING) $(MBRTOWC_LIB) $(LIBC32CONV)
+test_mbsstr2_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBC32CONV)
+test_mbsstr3_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) $(MBRTOWC_LIB) 
$(LIBC32CONV)
diff --git a/modules/mbstoc32s b/modules/mbstoc32s
index c32a4df683..1c554202c9 100644
--- a/modules/mbstoc32s
+++ b/modules/mbstoc32s
@@ -19,7 +19,9 @@ Include:
 <uchar.h>
 
 Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPLv2+
diff --git a/modules/mbstoc32s-tests b/modules/mbstoc32s-tests
index 9e6dd61d26..f7f6af8aa5 100644
--- a/modules/mbstoc32s-tests
+++ b/modules/mbstoc32s-tests
@@ -33,4 +33,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_JA='@LOCALE_JA@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-mbstoc32s
-test_mbstoc32s_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_mbstoc32s_LDADD = $(LDADD) $(LIBUNISTRING) $(SETLOCALE_LIB) 
$(MBRTOWC_LIB) $(LIBC32CONV)
diff --git a/modules/mbstok_r b/modules/mbstok_r
index cc420ca196..6656bbad72 100644
--- a/modules/mbstok_r
+++ b/modules/mbstok_r
@@ -23,6 +23,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/mbuiter b/modules/mbuiter
index 63a11ff2f5..23b1b20d04 100644
--- a/modules/mbuiter
+++ b/modules/mbuiter
@@ -28,6 +28,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 LGPL
diff --git a/modules/propername b/modules/propername
index 2a63a0eaf0..8ee9c5e320 100644
--- a/modules/propername
+++ b/modules/propername
@@ -32,6 +32,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 GPL
diff --git a/modules/regex-quote b/modules/regex-quote
index 43ddc49707..c88d14f95a 100644
--- a/modules/regex-quote
+++ b/modules/regex-quote
@@ -22,6 +22,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 GPL
diff --git a/modules/regex-quote-tests b/modules/regex-quote-tests
index 85739b57e9..9c5091970f 100644
--- a/modules/regex-quote-tests
+++ b/modules/regex-quote-tests
@@ -11,4 +11,4 @@ configure.ac:
 Makefile.am:
 TESTS += test-regex-quote
 check_PROGRAMS += test-regex-quote
-test_regex_quote_LDADD = $(LDADD) $(LIBUNISTRING) $(LIBINTL) $(MBRTOWC_LIB) 
$(LIBTHREAD)
+test_regex_quote_LDADD = $(LDADD) $(LIBUNISTRING) $(LIBINTL) $(MBRTOWC_LIB) 
$(LIBC32CONV) $(LIBTHREAD)
diff --git a/modules/trim b/modules/trim
index a4483c2f65..2de4d6fb01 100644
--- a/modules/trim
+++ b/modules/trim
@@ -23,6 +23,7 @@ Include:
 Link:
 $(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
 $(MBRTOWC_LIB)
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
 
 License:
 GPL
diff --git a/modules/trim-tests b/modules/trim-tests
index ece746d2df..12c12cba7b 100644
--- a/modules/trim-tests
+++ b/modules/trim-tests
@@ -19,4 +19,4 @@ TESTS_ENVIRONMENT += \
   LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' \
   LOCALE_ZH_CN='@LOCALE_ZH_CN@'
 check_PROGRAMS += test-trim
-test_trim_LDADD = $(LDADD) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB)
+test_trim_LDADD = $(LDADD) $(LIBUNISTRING) @LIBINTL@ $(MBRTOWC_LIB) 
$(LIBC32CONV)
diff --git a/modules/uchar-c23 b/modules/uchar-c23
new file mode 100644
index 0000000000..11e08652cb
--- /dev/null
+++ b/modules/uchar-c23
@@ -0,0 +1,57 @@
+Description:
+A <uchar.h> in which the char32_t values are always Unicode code points.
+
+Files:
+lib/lc-charset-unicode.h
+lib/lc-charset-unicode.c
+
+Depends-on:
+uchar
+wchar
+iconv
+localcharset
+streq
+lock
+tls
+wcrtomb
+unistr/u8-mbtouc
+unistr/u8-uctomb
+mbrtowc
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_REQUIRE([AM_ICONV])
+AC_DEFINE([GL_CHAR32_T_IS_UNICODE], [1],
+  [Define if gnulib's char32_t values are always Unicode code points.])
+dnl On macOS, FreeBSD, NetBSD, Solaris, the functions mbrtoc32 and c32rtomb
+dnl need to convert between the wchar_t encoding and Unicode.
+case "$host_os" in
+  darwin* | freebsd* | dragonfly* | netbsd* | solaris*)
+    AC_DEFINE([GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION], [1],
+      [Define if gnulib needs to convert between the wchar_t encoding and 
Unicode.])
+    LIBC32CONV="$LIBICONV"
+    LTLIBC32CONV="$LTLIBICONV"
+    ;;
+  *)
+    LIBC32CONV=
+    LTLIBC32CONV=
+    ;;
+esac
+AC_SUBST([LIBC32CONV])
+AC_SUBST([LTLIBC32CONV])
+
+Makefile.am:
+lib_SOURCES += lc-charset-unicode.c
+
+Include:
+<uchar.h>
+
+Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
+$(LTLIBC32CONV) when linking with libtool, $(LIBC32CONV) otherwise
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/tests/test-c32isalnum.c b/tests/test-c32isalnum.c
index 1d43fe261f..9805c1331f 100644
--- a/tests/test-c32isalnum.c
+++ b/tests/test-c32isalnum.c
@@ -212,6 +212,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+00D7 MULTIPLICATION SIGN */
           is = for_character ("\241\301", 2);
diff --git a/tests/test-c32isalnum.sh b/tests/test-c32isalnum.sh
index 9c8ec4223b..df94aaded0 100755
--- a/tests/test-c32isalnum.sh
+++ b/tests/test-c32isalnum.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32isalnum${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32isalnum${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32isalpha.c b/tests/test-c32isalpha.c
index 90f79192a0..31c98bb76f 100644
--- a/tests/test-c32isalpha.c
+++ b/tests/test-c32isalpha.c
@@ -158,7 +158,7 @@ main (int argc, char *argv[])
           /* U+3001 IDEOGRAPHIC COMMA */
           is = for_character ("\241\242", 2);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined __CYGWIN__)
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__ || defined __NetBSD__ || defined __sun || defined 
__CYGWIN__)
           /* U+FF11 FULLWIDTH DIGIT ONE */
           is = for_character ("\243\261", 2);
           ASSERT (is == 0);
@@ -186,7 +186,7 @@ main (int argc, char *argv[])
           /* U+3001 IDEOGRAPHIC COMMA */
           is = for_character ("\343\200\201", 3);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined _AIX || 
defined __sun || defined __CYGWIN__)
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ || defined _AIX 
|| defined __sun || defined __CYGWIN__)
           /* U+FF11 FULLWIDTH DIGIT ONE */
           is = for_character ("\357\274\221", 3);
           ASSERT (is == 0);
@@ -210,6 +210,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+00D7 MULTIPLICATION SIGN */
           is = for_character ("\241\301", 2);
@@ -225,7 +229,7 @@ main (int argc, char *argv[])
           /* U+3001 IDEOGRAPHIC COMMA */
           is = for_character ("\241\242", 2);
           ASSERT (is == 0);
-        #if !defined __GLIBC__
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__)
           /* U+FF11 FULLWIDTH DIGIT ONE */
           is = for_character ("\243\261", 2);
           ASSERT (is == 0);
diff --git a/tests/test-c32isalpha.sh b/tests/test-c32isalpha.sh
index 994ac3d8aa..11c896fa60 100755
--- a/tests/test-c32isalpha.sh
+++ b/tests/test-c32isalpha.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32isalpha${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32isalpha${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32isblank.c b/tests/test-c32isblank.c
index 1f5a7747cb..ac6f4251eb 100644
--- a/tests/test-c32isblank.c
+++ b/tests/test-c32isblank.c
@@ -169,6 +169,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
         #if defined __GLIBC__
           /* U+00A0 NO-BREAK SPACE */
diff --git a/tests/test-c32isblank.sh b/tests/test-c32isblank.sh
index 023dfc4829..fe5f9b86bf 100755
--- a/tests/test-c32isblank.sh
+++ b/tests/test-c32isblank.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32isblank${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32isblank${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32iscntrl.c b/tests/test-c32iscntrl.c
index dd935ac8c1..b47f936134 100644
--- a/tests/test-c32iscntrl.c
+++ b/tests/test-c32iscntrl.c
@@ -145,7 +145,7 @@ main (int argc, char *argv[])
           /* U+00A0 NO-BREAK SPACE */
           is = for_character ("\302\240", 2);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined __FreeBSD__ || 
defined __DragonFly__ || defined _AIX || defined __sun || defined __CYGWIN__)
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __DragonFly__ || defined 
__NetBSD__ || defined _AIX || defined __sun || defined __CYGWIN__)
           /* U+202E RIGHT-TO-LEFT OVERRIDE */
           is = for_character ("\342\200\256", 3);
           ASSERT (is != 0);
@@ -153,7 +153,7 @@ main (int argc, char *argv[])
           /* U+3000 IDEOGRAPHIC SPACE */
           is = for_character ("\343\200\200", 3);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined __FreeBSD__ || 
defined __DragonFly__ || defined _AIX || defined __sun || defined __CYGWIN__)
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __DragonFly__ || defined 
__NetBSD__ || defined _AIX || defined __sun || defined __CYGWIN__)
           /* U+FEFF ZERO WIDTH NO-BREAK SPACE */
           is = for_character ("\357\273\277", 3);
           ASSERT (is != 0);
@@ -161,7 +161,7 @@ main (int argc, char *argv[])
           /* U+20000 <CJK Ideograph> */
           is = for_character ("\360\240\200\200", 4);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined __FreeBSD__ || 
defined __DragonFly__ || defined _AIX || defined __sun || defined __CYGWIN__ || 
(defined _WIN32 && !defined __CYGWIN__))
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __DragonFly__ || defined 
__NetBSD__ || defined _AIX || defined __sun || defined __CYGWIN__ || (defined 
_WIN32 && !defined __CYGWIN__))
           /* U+E0001 LANGUAGE TAG */
           is = for_character ("\363\240\200\201", 4);
           ASSERT (is != 0);
@@ -171,6 +171,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+007F <control> */
           is = for_character ("\177", 1);
@@ -178,7 +182,7 @@ main (int argc, char *argv[])
           /* U+00A0 NO-BREAK SPACE */
           is = for_character ("\201\060\204\062", 4);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined __FreeBSD__ || defined 
__DragonFly__ || defined __sun)
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__ || defined __DragonFly__ || defined __sun)
           /* U+202E RIGHT-TO-LEFT OVERRIDE */
           is = for_character ("\201\066\247\061", 4);
           ASSERT (is != 0);
@@ -186,7 +190,7 @@ main (int argc, char *argv[])
           /* U+3000 IDEOGRAPHIC SPACE */
           is = for_character ("\241\241", 2);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined __FreeBSD__ || defined 
__DragonFly__ || defined __sun)
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__ || defined __DragonFly__ || defined __sun)
           /* U+FEFF ZERO WIDTH NO-BREAK SPACE */
           is = for_character ("\204\061\225\063", 4);
           ASSERT (is != 0);
diff --git a/tests/test-c32iscntrl.sh b/tests/test-c32iscntrl.sh
index 555b4de632..b27747c726 100755
--- a/tests/test-c32iscntrl.sh
+++ b/tests/test-c32iscntrl.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32iscntrl${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32iscntrl${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32isdigit.c b/tests/test-c32isdigit.c
index 73df34b24a..a2a4ab1623 100644
--- a/tests/test-c32isdigit.c
+++ b/tests/test-c32isdigit.c
@@ -183,6 +183,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+00B2 SUPERSCRIPT TWO */
           is = for_character ("\201\060\205\065", 4);
diff --git a/tests/test-c32isdigit.sh b/tests/test-c32isdigit.sh
index 6033defc98..158d362872 100755
--- a/tests/test-c32isdigit.sh
+++ b/tests/test-c32isdigit.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32isdigit${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32isdigit${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32isgraph.c b/tests/test-c32isgraph.c
index 5edf44d465..6ee7a1c866 100644
--- a/tests/test-c32isgraph.c
+++ b/tests/test-c32isgraph.c
@@ -166,7 +166,7 @@ main (int argc, char *argv[])
           /* U+2002 EN SPACE */
           is = for_character ("\342\200\202", 3);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined _AIX || 
defined __sun || defined __CYGWIN__)
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ || defined _AIX 
|| defined __sun || defined __CYGWIN__)
           /* U+202E RIGHT-TO-LEFT OVERRIDE */
           is = for_character ("\342\200\256", 3);
           ASSERT (is == 0);
@@ -174,7 +174,7 @@ main (int argc, char *argv[])
           /* U+3000 IDEOGRAPHIC SPACE */
           is = for_character ("\343\200\200", 3);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined _AIX || 
defined __sun || defined __CYGWIN__)
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ || defined _AIX 
|| defined __sun || defined __CYGWIN__)
           /* U+FEFF ZERO WIDTH NO-BREAK SPACE */
           is = for_character ("\357\273\277", 3);
           ASSERT (is == 0);
@@ -184,7 +184,7 @@ main (int argc, char *argv[])
           is = for_character ("\360\240\200\200", 4);
           ASSERT (is != 0);
         #endif
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined _AIX || 
defined __sun || defined __CYGWIN__ || (defined _WIN32 && !defined __CYGWIN__))
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ || defined _AIX 
|| defined __sun || defined __CYGWIN__ || (defined _WIN32 && !defined 
__CYGWIN__))
           /* U+E0001 LANGUAGE TAG */
           is = for_character ("\363\240\200\201", 4);
           ASSERT (is == 0);
@@ -194,6 +194,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+007F <control> */
           is = for_character ("\177", 1);
@@ -211,7 +215,7 @@ main (int argc, char *argv[])
           /* U+2002 EN SPACE */
           is = for_character ("\201\066\243\070", 4);
           ASSERT (is == 0);
-        #if !defined __GLIBC__
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__)
           /* U+202E RIGHT-TO-LEFT OVERRIDE */
           is = for_character ("\201\066\247\061", 4);
           ASSERT (is == 0);
@@ -219,7 +223,7 @@ main (int argc, char *argv[])
           /* U+3000 IDEOGRAPHIC SPACE */
           is = for_character ("\241\241", 2);
           ASSERT (is == 0);
-        #if !defined __GLIBC__
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__)
           /* U+FEFF ZERO WIDTH NO-BREAK SPACE */
           is = for_character ("\204\061\225\063", 4);
           ASSERT (is == 0);
@@ -229,7 +233,7 @@ main (int argc, char *argv[])
           is = for_character ("\225\062\202\066", 4);
           ASSERT (is != 0);
         #endif
-        #if !defined __GLIBC__
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__)
           /* U+E0001 LANGUAGE TAG */
           is = for_character ("\323\066\225\071", 4);
           ASSERT (is == 0);
diff --git a/tests/test-c32isgraph.sh b/tests/test-c32isgraph.sh
index 4541aee263..be760fb2f3 100755
--- a/tests/test-c32isgraph.sh
+++ b/tests/test-c32isgraph.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32isgraph${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32isgraph${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32islower.c b/tests/test-c32islower.c
index 6d4e222bc3..41e0e27cb6 100644
--- a/tests/test-c32islower.c
+++ b/tests/test-c32islower.c
@@ -128,7 +128,7 @@ main (int argc, char *argv[])
           /* U+00B2 SUPERSCRIPT TWO */
           is = for_character ("\262", 1);
           ASSERT (is == 0);
-        #if !(defined __GLIBC__ || defined __sun || defined __CYGWIN__ || 
(defined _WIN32 && !defined __CYGWIN__))
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__ || defined __NetBSD__ || defined __sun || defined 
__CYGWIN__ || (defined _WIN32 && !defined __CYGWIN__))
           /* U+00B5 MICRO SIGN */
           is = for_character ("\265", 1);
           ASSERT (is == 0);
@@ -267,6 +267,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+00B2 SUPERSCRIPT TWO */
           is = for_character ("\201\060\205\065", 4);
diff --git a/tests/test-c32islower.sh b/tests/test-c32islower.sh
index a88602ad5a..f8b9840ce0 100755
--- a/tests/test-c32islower.sh
+++ b/tests/test-c32islower.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32islower${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32islower${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32isprint.c b/tests/test-c32isprint.c
index 538c371f16..b7617ee105 100644
--- a/tests/test-c32isprint.c
+++ b/tests/test-c32isprint.c
@@ -169,7 +169,7 @@ main (int argc, char *argv[])
           is = for_character ("\342\200\202", 3);
           ASSERT (is != 0);
         #endif
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined _AIX || 
defined __sun || defined __CYGWIN__)
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ || defined _AIX 
|| defined __sun || defined __CYGWIN__)
           /* U+202E RIGHT-TO-LEFT OVERRIDE */
           is = for_character ("\342\200\256", 3);
           ASSERT (is == 0);
@@ -179,7 +179,7 @@ main (int argc, char *argv[])
           is = for_character ("\343\200\200", 3);
           ASSERT (is != 0);
         #endif
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined _AIX || 
defined __sun || defined __CYGWIN__ || (defined _WIN32 && !defined __CYGWIN__))
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ || defined _AIX 
|| defined __sun || defined __CYGWIN__ || (defined _WIN32 && !defined 
__CYGWIN__))
           /* U+FEFF ZERO WIDTH NO-BREAK SPACE */
           is = for_character ("\357\273\277", 3);
           ASSERT (is == 0);
@@ -189,7 +189,7 @@ main (int argc, char *argv[])
           is = for_character ("\360\240\200\200", 4);
           ASSERT (is != 0);
         #endif
-        #if !(defined __GLIBC__ || defined MUSL_LIBC || defined _AIX || 
defined __sun || defined __CYGWIN__ || (defined _WIN32 && !defined __CYGWIN__))
+        #if !(defined __GLIBC__ || defined MUSL_LIBC || (defined __APPLE__ && 
defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ || defined _AIX 
|| defined __sun || defined __CYGWIN__ || (defined _WIN32 && !defined 
__CYGWIN__))
           /* U+E0001 LANGUAGE TAG */
           is = for_character ("\363\240\200\201", 4);
           ASSERT (is == 0);
@@ -199,6 +199,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+007F <control> */
           is = for_character ("\177", 1);
@@ -214,7 +218,7 @@ main (int argc, char *argv[])
           is = for_character ("\201\066\243\070", 4);
           ASSERT (is != 0);
         #endif
-        #if !defined __GLIBC__
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__)
           /* U+202E RIGHT-TO-LEFT OVERRIDE */
           is = for_character ("\201\066\247\061", 4);
           ASSERT (is == 0);
@@ -224,7 +228,7 @@ main (int argc, char *argv[])
           is = for_character ("\241\241", 2);
           ASSERT (is != 0);
         #endif
-        #if !defined __GLIBC__
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__)
           /* U+FEFF ZERO WIDTH NO-BREAK SPACE */
           is = for_character ("\204\061\225\063", 4);
           ASSERT (is == 0);
@@ -234,7 +238,7 @@ main (int argc, char *argv[])
           is = for_character ("\225\062\202\066", 4);
           ASSERT (is != 0);
         #endif
-        #if !defined __GLIBC__
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__)
           /* U+E0001 LANGUAGE TAG */
           is = for_character ("\323\066\225\071", 4);
           ASSERT (is == 0);
diff --git a/tests/test-c32isprint.sh b/tests/test-c32isprint.sh
index 1d8d455d75..d455413ba3 100755
--- a/tests/test-c32isprint.sh
+++ b/tests/test-c32isprint.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32isprint${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32isprint${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32ispunct.c b/tests/test-c32ispunct.c
index 168ad283b9..c655b6317a 100644
--- a/tests/test-c32ispunct.c
+++ b/tests/test-c32ispunct.c
@@ -246,7 +246,7 @@ main (int argc, char *argv[])
           is = for_character ("\360\235\204\200", 4);
           ASSERT (is != 0);
         #endif
-        #if !(defined __GLIBC__ || defined _AIX || defined __sun || defined 
__CYGWIN__ || (defined _WIN32 && !defined __CYGWIN__))
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__ || defined __NetBSD__ || defined _AIX || defined __sun || 
defined __CYGWIN__ || (defined _WIN32 && !defined __CYGWIN__))
           /* U+E003A TAG COLON */
           is = for_character ("\363\240\200\272", 4);
           ASSERT (is == 0);
@@ -256,6 +256,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
         #if !(defined __FreeBSD__ || defined __DragonFly__ || defined __sun)
           /* U+00BF INVERTED QUESTION MARK */
@@ -303,7 +307,7 @@ main (int argc, char *argv[])
           is = for_character ("\224\062\273\064", 4);
           ASSERT (is != 0);
         #endif
-        #if !defined __GLIBC__
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__)
           /* U+E003A TAG COLON */
           is = for_character ("\323\066\233\066", 4);
           ASSERT (is == 0);
diff --git a/tests/test-c32ispunct.sh b/tests/test-c32ispunct.sh
index b0a5870959..d818bcf734 100755
--- a/tests/test-c32ispunct.sh
+++ b/tests/test-c32ispunct.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32ispunct${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32ispunct${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32isspace.c b/tests/test-c32isspace.c
index 86170d9edc..da6335802c 100644
--- a/tests/test-c32isspace.c
+++ b/tests/test-c32isspace.c
@@ -165,6 +165,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+00B7 MIDDLE DOT */
           is = for_character ("\241\244", 2);
diff --git a/tests/test-c32isspace.sh b/tests/test-c32isspace.sh
index 730b9b7253..a704d86bfc 100755
--- a/tests/test-c32isspace.sh
+++ b/tests/test-c32isspace.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32isspace${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32isspace${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32isupper.c b/tests/test-c32isupper.c
index 465a544664..31daad64c0 100644
--- a/tests/test-c32isupper.c
+++ b/tests/test-c32isupper.c
@@ -259,6 +259,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+00B2 SUPERSCRIPT TWO */
           is = for_character ("\201\060\205\065", 4);
diff --git a/tests/test-c32isupper.sh b/tests/test-c32isupper.sh
index da5fcab22a..ea3efcb845 100755
--- a/tests/test-c32isupper.sh
+++ b/tests/test-c32isupper.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32isupper${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32isupper${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32isxdigit.c b/tests/test-c32isxdigit.c
index e4359f779f..0fec8a4a4b 100644
--- a/tests/test-c32isxdigit.c
+++ b/tests/test-c32isxdigit.c
@@ -200,6 +200,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+00B2 SUPERSCRIPT TWO */
           is = for_character ("\201\060\205\065", 4);
diff --git a/tests/test-c32isxdigit.sh b/tests/test-c32isxdigit.sh
index e78109bdb4..48f8efa732 100755
--- a/tests/test-c32isxdigit.sh
+++ b/tests/test-c32isxdigit.sh
@@ -32,8 +32,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32isxdigit${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32isxdigit${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32rtomb.c b/tests/test-c32rtomb.c
index bb4b4bec0b..8969873181 100644
--- a/tests/test-c32rtomb.c
+++ b/tests/test-c32rtomb.c
@@ -152,6 +152,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           const char input[] = "s\250\271\201\060\211\070\224\071\375\067!"; 
/* "süß😋!" */
 
diff --git a/tests/test-c32rtomb.sh b/tests/test-c32rtomb.sh
index d2b091968b..3239935332 100755
--- a/tests/test-c32rtomb.sh
+++ b/tests/test-c32rtomb.sh
@@ -28,8 +28,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32rtomb${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32rtomb${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 # Test in the POSIX locale.
diff --git a/tests/test-c32snrtombs.c b/tests/test-c32snrtombs.c
index 3e397882f3..ef09679524 100644
--- a/tests/test-c32snrtombs.c
+++ b/tests/test-c32snrtombs.c
@@ -160,6 +160,10 @@ main (int argc, char *argv[])
 
         case '4':
           /* Locale encoding is GB18030.  */
+          #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+          fputs ("Skipping test: The GB18030 converter in this system's iconv 
is broken.\n", stderr);
+          return 77;
+          #endif
           {
             const char original[] = 
"s\250\271\201\060\211\070\224\071\375\067!"; /* "süß😋!" */
 
diff --git a/tests/test-c32srtombs.c b/tests/test-c32srtombs.c
index 1b856a992c..3d5c67a77f 100644
--- a/tests/test-c32srtombs.c
+++ b/tests/test-c32srtombs.c
@@ -160,6 +160,10 @@ main (int argc, char *argv[])
 
         case '4':
           /* Locale encoding is GB18030.  */
+          #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+          fputs ("Skipping test: The GB18030 converter in this system's iconv 
is broken.\n", stderr);
+          return 77;
+          #endif
           {
             const char original[] = 
"s\250\271\201\060\211\070\224\071\375\067!"; /* "süß😋!" */
 
diff --git a/tests/test-c32stombs.c b/tests/test-c32stombs.c
index d54bc37802..53339d1fef 100644
--- a/tests/test-c32stombs.c
+++ b/tests/test-c32stombs.c
@@ -139,6 +139,10 @@ main (int argc, char *argv[])
 
         case '4':
           /* Locale encoding is GB18030.  */
+          #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+          fputs ("Skipping test: The GB18030 converter in this system's iconv 
is broken.\n", stderr);
+          return 77;
+          #endif
           {
             const char original[] = 
"s\250\271\201\060\211\070\224\071\375\067!"; /* "süß😋!" */
 
diff --git a/tests/test-c32tolower.c b/tests/test-c32tolower.c
index 30fe9b5848..1d35abb2bc 100644
--- a/tests/test-c32tolower.c
+++ b/tests/test-c32tolower.c
@@ -336,6 +336,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+00B2 SUPERSCRIPT TWO */
           mb = for_character ("\201\060\205\065", 4);
diff --git a/tests/test-c32tolower.sh b/tests/test-c32tolower.sh
index a0859a900e..02f3e5f655 100755
--- a/tests/test-c32tolower.sh
+++ b/tests/test-c32tolower.sh
@@ -35,8 +35,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32tolower${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32tolower${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-c32toupper.c b/tests/test-c32toupper.c
index af27bbca09..4ec6431371 100644
--- a/tests/test-c32toupper.c
+++ b/tests/test-c32toupper.c
@@ -163,7 +163,7 @@ main (int argc, char *argv[])
           mb = for_character ("\262", 1);
           ASSERT (mb.nbytes == 1);
           ASSERT (memcmp (mb.buf, "\262", 1) == 0);
-        #if !(defined __GLIBC__ || defined __sun || defined __CYGWIN__)
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__ || defined __NetBSD__ || defined __sun || defined 
__CYGWIN__)
           /* U+00B5 MICRO SIGN */
           mb = for_character ("\265", 1);
           ASSERT (mb.nbytes == 1);
@@ -181,7 +181,7 @@ main (int argc, char *argv[])
           mb = for_character ("\351", 1);
           ASSERT (mb.nbytes == 1);
           ASSERT (memcmp (mb.buf, "\311", 1) == 0);
-        #if !(defined __GLIBC__ || defined __DragonFly__ || defined __sun || 
defined __CYGWIN__ || (defined _WIN32 && !defined __CYGWIN__))
+        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ || defined 
__sun || defined __CYGWIN__ || (defined _WIN32 && !defined __CYGWIN__))
           /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
           mb = for_character ("\377", 1);
           ASSERT (mb.nbytes == 1);
@@ -348,12 +348,16 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           /* U+00B2 SUPERSCRIPT TWO */
           mb = for_character ("\201\060\205\065", 4);
           ASSERT (mb.nbytes == 4);
           ASSERT (memcmp (mb.buf, "\201\060\205\065", 4) == 0);
-        #if !(defined __GLIBC__ || (defined __APPLE__ && defined __MACH__) || 
defined __NetBSD__)
+        #if !(defined __GLIBC__ || defined __FreeBSD__ || (defined __APPLE__ 
&& defined __MACH__) || defined __NetBSD__)
           /* U+00B5 MICRO SIGN */
           mb = for_character ("\201\060\205\070", 4);
           ASSERT (mb.nbytes == 4);
diff --git a/tests/test-c32toupper.sh b/tests/test-c32toupper.sh
index 1c253e5460..6eed37d908 100755
--- a/tests/test-c32toupper.sh
+++ b/tests/test-c32toupper.sh
@@ -35,8 +35,11 @@ fi
 : "${LOCALE_ZH_CN=zh_CN.GB18030}"
 if test $LOCALE_ZH_CN != none; then
   LC_ALL=$LOCALE_ZH_CN \
-  ${CHECKER} ./test-c32toupper${EXEEXT} 4 \
-  || exit 1
+  ${CHECKER} ./test-c32toupper${EXEEXT} 4
+  case $? in
+    0 | 77) ;;
+    *) exit 1 ;;
+  esac
 fi
 
 exit 0
diff --git a/tests/test-mbrtoc32-w32.c b/tests/test-mbrtoc32-w32.c
index 403ab69ba2..686d501ed7 100644
--- a/tests/test-mbrtoc32-w32.c
+++ b/tests/test-mbrtoc32-w32.c
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <wchar.h>
 
 #include "localcharset.h"
 #include "macros.h"
@@ -90,10 +91,7 @@ test_one_locale (const char *name, int codepage)
     memset (&state, '\0', sizeof (mbstate_t));
     wc = (char32_t) 0xBADFACE;
     ret = mbrtoc32 (&wc, "x", 0, &state);
-    /* gnulib's implementation returns (size_t)(-2).
-       The AIX 5.1 implementation returns (size_t)(-1).
-       glibc's implementation returns 0.  */
-    ASSERT (ret == (size_t)(-2) || ret == (size_t)(-1) || ret == 0);
+    ASSERT (ret == (size_t)(-2));
     ASSERT (mbsinit (&state));
   }
 
@@ -183,7 +181,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 1, 1, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == (unsigned char) '\374');
-        ASSERT (wc == 0x00FC);
+        ASSERT (wc == 0x00FC); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[1] = '\0';
 
@@ -196,7 +194,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 2, 3, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == (unsigned char) '\337');
-        ASSERT (wc == 0x00DF);
+        ASSERT (wc == 0x00DF); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[2] = '\0';
 
@@ -232,7 +230,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 1, 1, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == (unsigned char) '\302');
-        ASSERT (wc == 0x0622);
+        ASSERT (wc == 0x0622); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[1] = '\0';
 
@@ -245,7 +243,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 2, 3, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == (unsigned char) '\341');
-        ASSERT (wc == 0x0644);
+        ASSERT (wc == 0x0644); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[2] = '\0';
 
@@ -253,7 +251,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 3, 2, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == (unsigned char) '\346');
-        ASSERT (wc == 0x0648);
+        ASSERT (wc == 0x0648); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[3] = '\0';
 
@@ -282,7 +280,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 1, 2, &state);
         ASSERT (ret == 2);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x65E5);
+        ASSERT (wc == 0x65E5); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[1] = '\0';
         input[2] = '\0';
@@ -298,7 +296,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 4, 4, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x672C);
+        ASSERT (wc == 0x672C); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[4] = '\0';
 
@@ -311,7 +309,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 5, 3, &state);
         ASSERT (ret == 2);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x8A9E);
+        ASSERT (wc == 0x8A9E); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[5] = '\0';
         input[6] = '\0';
@@ -352,7 +350,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 1, 2, &state);
         ASSERT (ret == 2);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x65E5);
+        ASSERT (wc == 0x65E5); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[1] = '\0';
         input[2] = '\0';
@@ -368,7 +366,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 4, 4, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x672C);
+        ASSERT (wc == 0x672C); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[4] = '\0';
 
@@ -381,7 +379,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 5, 3, &state);
         ASSERT (ret == 2);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x8A9E);
+        ASSERT (wc == 0x8A9E); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[5] = '\0';
         input[6] = '\0';
@@ -422,7 +420,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 1, 2, &state);
         ASSERT (ret == 2);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x65E5);
+        ASSERT (wc == 0x65E5); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[1] = '\0';
         input[2] = '\0';
@@ -438,7 +436,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 4, 4, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x672C);
+        ASSERT (wc == 0x672C); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[4] = '\0';
 
@@ -451,7 +449,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 5, 3, &state);
         ASSERT (ret == 2);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x8A9E);
+        ASSERT (wc == 0x8A9E); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[5] = '\0';
         input[6] = '\0';
@@ -501,7 +499,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 2, 9, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x00FC);
+        ASSERT (wc == 0x00FC); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[2] = '\0';
 
@@ -514,7 +512,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 3, 8, &state);
         ASSERT (ret == 4);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x00DF);
+        ASSERT (wc == 0x00DF); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[3] = '\0';
         input[4] = '\0';
@@ -530,7 +528,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 7, 4, &state);
         ASSERT (ret == 4);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x1F60B);
+        ASSERT (wc == 0x1F60B); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[7] = '\0';
         input[8] = '\0';
@@ -608,7 +606,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 2, 7, &state);
         ASSERT (ret == 1);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x00FC);
+        ASSERT (wc == 0x00FC); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[2] = '\0';
 
@@ -621,7 +619,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 3, 6, &state);
         ASSERT (ret == 2);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x00DF);
+        ASSERT (wc == 0x00DF); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[3] = '\0';
         input[4] = '\0';
@@ -635,7 +633,7 @@ test_one_locale (const char *name, int codepage)
         ret = mbrtoc32 (&wc, input + 5, 4, &state);
         ASSERT (ret == 4);
         ASSERT (c32tob (wc) == EOF);
-        ASSERT (wc == 0x1F60B);
+        ASSERT (wc == 0x1F60B); /* expect Unicode encoding */
         ASSERT (mbsinit (&state));
         input[5] = '\0';
         input[6] = '\0';
diff --git a/tests/test-mbrtoc32.c b/tests/test-mbrtoc32.c
index ecde031f36..f5a51ef34e 100644
--- a/tests/test-mbrtoc32.c
+++ b/tests/test-mbrtoc32.c
@@ -28,6 +28,7 @@ SIGNATURE_CHECK (mbrtoc32, size_t,
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <wchar.h>
 
 #include "macros.h"
 
@@ -154,6 +155,9 @@ main (int argc, char *argv[])
           ret = mbrtoc32 (&wc, input + 1, 1, &state);
           ASSERT (ret == 1);
           ASSERT (c32tob (wc) == (unsigned char) '\374');
+          #if GL_CHAR32_T_IS_UNICODE
+          ASSERT (wc == 0x00FC); /* expect Unicode encoding */
+          #endif
           ASSERT (mbsinit (&state));
           input[1] = '\0';
 
@@ -166,6 +170,9 @@ main (int argc, char *argv[])
           ret = mbrtoc32 (&wc, input + 2, 3, &state);
           ASSERT (ret == 1);
           ASSERT (c32tob (wc) == (unsigned char) '\337');
+          #if GL_CHAR32_T_IS_UNICODE
+          ASSERT (wc == 0x00DF); /* expect Unicode encoding */
+          #endif
           ASSERT (mbsinit (&state));
           input[2] = '\0';
 
@@ -267,6 +274,9 @@ main (int argc, char *argv[])
           ret = mbrtoc32 (&wc, input + 1, 2, &state);
           ASSERT (ret == 2);
           ASSERT (c32tob (wc) == EOF);
+          #if GL_CHAR32_T_IS_UNICODE
+          ASSERT (wc == 0x65E5); /* expect Unicode encoding */
+          #endif
           ASSERT (mbsinit (&state));
           input[1] = '\0';
           input[2] = '\0';
@@ -282,6 +292,9 @@ main (int argc, char *argv[])
           ret = mbrtoc32 (&wc, input + 4, 4, &state);
           ASSERT (ret == 1);
           ASSERT (c32tob (wc) == EOF);
+          #if GL_CHAR32_T_IS_UNICODE
+          ASSERT (wc == 0x672C); /* expect Unicode encoding */
+          #endif
           ASSERT (mbsinit (&state));
           input[4] = '\0';
 
@@ -294,6 +307,9 @@ main (int argc, char *argv[])
           ret = mbrtoc32 (&wc, input + 5, 3, &state);
           ASSERT (ret == 2);
           ASSERT (c32tob (wc) == EOF);
+          #if GL_CHAR32_T_IS_UNICODE
+          ASSERT (wc == 0x8A9E); /* expect Unicode encoding */
+          #endif
           ASSERT (mbsinit (&state));
           input[5] = '\0';
           input[6] = '\0';
@@ -308,6 +324,10 @@ main (int argc, char *argv[])
 
       case '4':
         /* Locale encoding is GB18030.  */
+        #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined __sun)
+        fputs ("Skipping test: The GB18030 converter in this system's iconv is 
broken.\n", stderr);
+        return 77;
+        #endif
         {
           char input[] = "s\250\271\201\060\211\070\224\071\375\067!"; /* 
"süß😋!" */
           memset (&state, '\0', sizeof (mbstate_t));
@@ -330,6 +350,9 @@ main (int argc, char *argv[])
           ret = mbrtoc32 (&wc, input + 2, 9, &state);
           ASSERT (ret == 1);
           ASSERT (c32tob (wc) == EOF);
+          #if GL_CHAR32_T_IS_UNICODE
+          ASSERT (wc == 0x00FC); /* expect Unicode encoding */
+          #endif
           ASSERT (mbsinit (&state));
           input[2] = '\0';
 
@@ -342,6 +365,9 @@ main (int argc, char *argv[])
           ret = mbrtoc32 (&wc, input + 3, 8, &state);
           ASSERT (ret == 4);
           ASSERT (c32tob (wc) == EOF);
+          #if GL_CHAR32_T_IS_UNICODE
+          ASSERT (wc == 0x00DF); /* expect Unicode encoding */
+          #endif
           ASSERT (mbsinit (&state));
           input[3] = '\0';
           input[4] = '\0';
@@ -357,6 +383,9 @@ main (int argc, char *argv[])
           ret = mbrtoc32 (&wc, input + 7, 4, &state);
           ASSERT (ret == 4);
           ASSERT (c32tob (wc) == EOF);
+          #if GL_CHAR32_T_IS_UNICODE
+          ASSERT (wc == 0x1F60B); /* expect Unicode encoding */
+          #endif
           ASSERT (mbsinit (&state));
           input[7] = '\0';
           input[8] = '\0';
diff --git a/tests/test-mbsnrtoc32s.c b/tests/test-mbsnrtoc32s.c
index c18b66ab06..342626d2a2 100644
--- a/tests/test-mbsnrtoc32s.c
+++ b/tests/test-mbsnrtoc32s.c
@@ -27,6 +27,7 @@ SIGNATURE_CHECK (mbsnrtoc32s, size_t,
 #include <locale.h>
 #include <stdio.h>
 #include <string.h>
+#include <wchar.h>
 
 #include "macros.h"
 
@@ -246,6 +247,10 @@ main (int argc, char *argv[])
 
             case '4':
               /* Locale encoding is GB18030.  */
+              #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined 
__sun)
+              fputs ("Skipping test: The GB18030 converter in this system's 
iconv is broken.\n", stderr);
+              return 77;
+              #endif
               {
                 char input[] = "s\250\271\201\060\211\070\224\071\375\067!"; 
/* "süß😋!" */
                 memset (&state, '\0', sizeof (mbstate_t));
diff --git a/tests/test-mbsrtoc32s.c b/tests/test-mbsrtoc32s.c
index aed9d7b2fb..b1576d8da5 100644
--- a/tests/test-mbsrtoc32s.c
+++ b/tests/test-mbsrtoc32s.c
@@ -27,6 +27,7 @@ SIGNATURE_CHECK (mbsrtoc32s, size_t,
 #include <locale.h>
 #include <stdio.h>
 #include <string.h>
+#include <wchar.h>
 
 #include "macros.h"
 
@@ -246,6 +247,10 @@ main (int argc, char *argv[])
 
             case '4':
               /* Locale encoding is GB18030.  */
+              #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined 
__sun)
+              fputs ("Skipping test: The GB18030 converter in this system's 
iconv is broken.\n", stderr);
+              return 77;
+              #endif
               {
                 char input[] = "s\250\271\201\060\211\070\224\071\375\067!"; 
/* "süß😋!" */
                 memset (&state, '\0', sizeof (mbstate_t));
diff --git a/tests/test-mbstoc32s.c b/tests/test-mbstoc32s.c
index e638999bb6..09a1e072cf 100644
--- a/tests/test-mbstoc32s.c
+++ b/tests/test-mbstoc32s.c
@@ -206,6 +206,10 @@ main (int argc, char *argv[])
 
             case '4':
               /* Locale encoding is GB18030.  */
+              #if GL_CHAR32_T_IS_UNICODE && (defined __NetBSD__ || defined 
__sun)
+              fputs ("Skipping test: The GB18030 converter in this system's 
iconv is broken.\n", stderr);
+              return 77;
+              #endif
               {
                 char input[] = "s\250\271\201\060\211\070\224\071\375\067!"; 
/* "süß😋!" */
 






reply via email to

[Prev in Thread] Current Thread [Next in Thread]