# mbrtoc16.m4 serial 2 dnl Copyright (C) 2014-2024 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([gl_FUNC_MBRTOC16], [ AC_REQUIRE([gl_UCHAR_H_DEFAULTS]) AC_REQUIRE([AC_TYPE_MBSTATE_T]) dnl Determine REPLACE_MBSTATE_T, from which GNULIB_defined_mbstate_t is dnl determined. It describes how our overridden mbrtowc is implemented. dnl We then implement mbrtoc16 accordingly. AC_REQUIRE([gl_MBSTATE_T_BROKEN]) AC_REQUIRE([gl_TYPE_CHAR16_T]) AC_REQUIRE([gl_MBRTOC16_SANITYCHECK]) AC_REQUIRE([gl_CHECK_FUNC_MBRTOC16]) if test $gl_cv_func_mbrtoc16 = no; then HAVE_MBRTOC16=0 else if test $GNULIBHEADERS_OVERRIDE_CHAR16_T = 1 || test $REPLACE_MBSTATE_T = 1; then REPLACE_MBRTOC16=1 else gl_MBRTOC16_NULL_DESTINATION gl_MBRTOC16_RETVAL gl_MBRTOC16_EMPTY_INPUT gl_MBRTOC16_C_LOCALE case "$gl_cv_func_mbrtoc16_null_destination" in *yes) ;; *) REPLACE_MBRTOC16=1 ;; esac case "$gl_cv_func_mbrtoc16_retval" in *yes) ;; *) REPLACE_MBRTOC16=1 ;; esac case "$gl_cv_func_mbrtoc16_empty_input" in *yes) ;; *) REPLACE_MBRTOC16=1 ;; esac case "$gl_cv_func_mbrtoc16_C_locale_sans_EILSEQ" in *yes) ;; *) REPLACE_MBRTOC16=1 ;; esac fi if test $HAVE_WORKING_MBRTOC16 = 0; then REPLACE_MBRTOC16=1 fi fi ]) AC_DEFUN([gl_CHECK_FUNC_MBRTOC16], [ dnl Cf. gl_CHECK_FUNCS_ANDROID AC_CHECK_DECL([mbrtoc16], , , [[#ifdef __HAIKU__ #include #endif #include ]]) if test $ac_cv_have_decl_mbrtoc16 = yes; then AC_CHECK_FUNCS([mbrtoc16]) gl_cv_func_mbrtoc16="$ac_cv_func_mbrtoc16" else gl_cv_func_mbrtoc16=no fi ]) AC_DEFUN([gl_MBRTOC16_NULL_DESTINATION], [ AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([gl_TYPE_CHAR16_T]) AC_REQUIRE([gt_LOCALE_FR_UTF8]) AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles AC_CACHE_CHECK([whether mbrtoc16 supports a NULL destination], [gl_cv_func_mbrtoc16_null_destination], [ dnl Initial guess, used when cross-compiling or when no suitable locale dnl is present. changequote(,)dnl case "$host_os" in # Guess no on glibc systems. *-gnu* | gnu*) gl_cv_func_mbrtoc16_null_destination="guessing no" ;; # Guess yes otherwise. *) gl_cv_func_mbrtoc16_null_destination="guessing yes" ;; esac changequote([,])dnl if test $LOCALE_FR_UTF8 != none; then AC_RUN_IFELSE( [AC_LANG_SOURCE([[ #include #include #ifdef __HAIKU__ #include #endif #include int main (void) { if (setlocale (LC_ALL, "$LOCALE_FR_UTF8") == NULL return 1; mbstate_t state; size_t ret; char input[] = "\360\237\230\213"; /* U+1F60B */ memset (&state, '\0', sizeof (mbstate_t)); ret = mbrtoc16 (NULL, input, 4, &state); if (ret != 4) return 2; ret = mbrtoc16 (NULL, input + 4, 0, &state); if (ret != (size_t)(-3)) return 3; return 0; }]])], [gl_cv_func_mbrtoc16_null_destination=yes], [gl_cv_func_mbrtoc16_null_destination=no], [:]) fi ]) ]) dnl Test whether mbrtoc16, when parsing the end of a multibyte character, dnl correctly returns the number of bytes that were needed to complete the dnl character (not the total number of bytes of the multibyte character). dnl Also test whether mbrtoc16 returns a byte count when it has stored a dnl high surrogate and (size_t) -3 when it has stored a low surrogate. dnl Result is gl_cv_func_mbrtoc16_retval. AC_DEFUN([gl_MBRTOC16_RETVAL], [ AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_CACHE_CHECK([whether mbrtoc16 has a correct return value], [gl_cv_func_mbrtoc16_retval], [ dnl Initial guess, used when cross-compiling or when no suitable locale dnl is present. changequote(,)dnl case "$host_os" in # Guess no on Android. linux*-android*) gl_cv_func_mbrtoc16_retval="guessing no" ;; # Guess no on native Windows. mingw* | windows*) gl_cv_func_mbrtoc16_retval="guessing no" ;; # Guess yes otherwise. *) gl_cv_func_mbrtoc16_retval="guessing yes" ;; esac changequote([,])dnl AC_RUN_IFELSE( [AC_LANG_SOURCE([[ #include #include #include #ifdef __HAIKU__ #include #endif #include int main () { int result = 0; int found_some_locale = 0; /* This fails on Android. */ if (setlocale (LC_ALL, "en_US.UTF-8") != NULL) { char input[] = "\360\237\230\213"; /* U+1F60B */ mbstate_t state; char16_t wc; memset (&state, '\0', sizeof (mbstate_t)); if (mbrtoc16 (&wc, input, 4, &state) != 4 || mbrtoc16 (&wc, input + 4, 0, &state) != (size_t)(-3)) result |= 1; found_some_locale = 1; } /* This fails on native Windows. */ if (setlocale (LC_ALL, "Japanese_Japan.932") != NULL) { char input[] = "<\223\372\226\173\214\352>"; /* "<日本語>" */ mbstate_t state; char16_t wc; memset (&state, '\0', sizeof (mbstate_t)); if (mbrtoc16 (&wc, input + 3, 1, &state) == (size_t)(-2)) { input[3] = '\0'; if (mbrtoc16 (&wc, input + 4, 4, &state) != 1) result |= 2; } found_some_locale = 1; } if (setlocale (LC_ALL, "Chinese_Taiwan.950") != NULL) { char input[] = "<\244\351\245\273\273\171>"; /* "<日本語>" */ mbstate_t state; char16_t wc; memset (&state, '\0', sizeof (mbstate_t)); if (mbrtoc16 (&wc, input + 3, 1, &state) == (size_t)(-2)) { input[3] = '\0'; if (mbrtoc16 (&wc, input + 4, 4, &state) != 1) result |= 4; } found_some_locale = 1; } if (setlocale (LC_ALL, "Chinese_China.936") != NULL) { char input[] = "<\310\325\261\276\325\132>"; /* "<日本語>" */ mbstate_t state; char16_t wc; memset (&state, '\0', sizeof (mbstate_t)); if (mbrtoc16 (&wc, input + 3, 1, &state) == (size_t)(-2)) { input[3] = '\0'; if (mbrtoc16 (&wc, input + 4, 4, &state) != 1) result |= 8; } found_some_locale = 1; } return (found_some_locale ? result : 77); }]])], [gl_cv_func_mbrtoc16_retval=yes], [if test $? != 77; then gl_cv_func_mbrtoc16_retval=no fi ], [:]) ]) ]) dnl Test whether mbrtoc16 returns the correct value on empty input. AC_DEFUN([gl_MBRTOC16_EMPTY_INPUT], [ AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles AC_CACHE_CHECK([whether mbrtoc16 works on empty input], [gl_cv_func_mbrtoc16_empty_input], [ AC_RUN_IFELSE( [AC_LANG_SOURCE([[ #ifdef __HAIKU__ #include #endif #include static char16_t wc; static mbstate_t mbs; int main (void) { return mbrtoc16 (&wc, "", 0, &mbs) != (size_t) -2; }]])], [gl_cv_func_mbrtoc16_empty_input=yes], [gl_cv_func_mbrtoc16_empty_input=no], [case "$host_os" in # Guess no on glibc systems. *-gnu* | gnu*) gl_cv_func_mbrtoc16_empty_input="guessing no" ;; # Guess no on Android. linux*-android*) gl_cv_func_mbrtoc16_empty_input="guessing no" ;; *) gl_cv_func_mbrtoc16_empty_input="guessing yes" ;; esac ]) ]) ]) dnl dnl POSIX:2018 says regarding mbrtowc: "In the POSIX locale an [EILSEQ] error dnl cannot occur since all byte values are valid characters." It is reasonable dnl to expect mbrtoc16 to behave in the same way. AC_DEFUN([gl_MBRTOC16_C_LOCALE], [ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles AC_CACHE_CHECK([whether the C locale is free of encoding errors], [gl_cv_func_mbrtoc16_C_locale_sans_EILSEQ], [AC_RUN_IFELSE( [AC_LANG_PROGRAM( [[#include #include #ifdef __HAIKU__ #include #endif #include ]], [[ int i; char *locale = setlocale (LC_ALL, "C"); if (! locale) return 2; for (i = CHAR_MIN; i <= CHAR_MAX; i++) { char c = i; char16_t wc; mbstate_t mbs = { 0, }; size_t ss = mbrtoc16 (&wc, &c, 1, &mbs); if (1 < ss) return 3; } return 0; ]])], [gl_cv_func_mbrtoc16_C_locale_sans_EILSEQ=yes], [gl_cv_func_mbrtoc16_C_locale_sans_EILSEQ=no], [case "$host_os" in # Guess yes on native Windows. mingw* | windows*) gl_cv_func_mbrtoc16_C_locale_sans_EILSEQ="guessing yes" ;; *) gl_cv_func_mbrtoc16_C_locale_sans_EILSEQ="$gl_cross_guess_normal" ;; esac ]) ]) ]) dnl Test whether mbrtoc16 works not worse than mbrtowc. dnl Result is HAVE_WORKING_MBRTOC16. AC_DEFUN([gl_MBRTOC16_SANITYCHECK], [ AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([gl_TYPE_CHAR16_T]) AC_REQUIRE([gl_CHECK_FUNC_MBRTOC16]) AC_REQUIRE([gt_LOCALE_FR]) AC_REQUIRE([gt_LOCALE_ZH_CN]) AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles if test $GNULIBHEADERS_OVERRIDE_CHAR16_T = 1 || test $gl_cv_func_mbrtoc16 = no; then HAVE_WORKING_MBRTOC16=0 else AC_CACHE_CHECK([whether mbrtoc16 works as well as mbrtowc], [gl_cv_func_mbrtoc16_sanitycheck], [ dnl Initial guess, used when cross-compiling or when no suitable locale dnl is present. changequote(,)dnl case "$host_os" in # Guess no on FreeBSD, Solaris, native Windows. freebsd* | midnightbsd* | solaris* | mingw* | windows*) gl_cv_func_mbrtoc16_sanitycheck="guessing no" ;; # Guess yes otherwise. *) gl_cv_func_mbrtoc16_sanitycheck="guessing yes" ;; esac changequote([,])dnl if test $LOCALE_FR != none || test $LOCALE_ZH_CN != none; then AC_RUN_IFELSE( [AC_LANG_SOURCE([[ #include #include #include #include #ifdef __HAIKU__ #include #endif #include int main () { int result = 0; /* This fails on MSVC: mbrtoc16 returns (size_t)-1. mbrtowc returns 1 (correct). */ if (strcmp ("$LOCALE_FR", "none") != 0 && setlocale (LC_ALL, "$LOCALE_FR") != NULL) { mbstate_t state; wchar_t wc = (wchar_t) 0xBADF; memset (&state, '\0', sizeof (mbstate_t)); if (mbrtowc (&wc, "\374", 1, &state) == 1) { char16_t c16 = (wchar_t) 0xBADF; memset (&state, '\0', sizeof (mbstate_t)); if (mbrtoc16 (&c16, "\374", 1, &state) != 1) result |= 1; } } /* This fails on FreeBSD 13.2 and Solaris 11.4: mbrtoc16 returns (size_t)-2 or (size_t)-1. mbrtowc returns 4 (correct). */ if (strcmp ("$LOCALE_ZH_CN", "none") != 0 && setlocale (LC_ALL, "$LOCALE_ZH_CN") != NULL) { mbstate_t state; wchar_t wc = (wchar_t) 0xBADF; memset (&state, '\0', sizeof (mbstate_t)); if (mbrtowc (&wc, "\224\071\375\067", 4, &state) == 4) { char16_t c16 = (wchar_t) 0xBADF; memset (&state, '\0', sizeof (mbstate_t)); if (mbrtoc16 (&c16, "\224\071\375\067", 4, &state) != 4) result |= 2; } } return result; }]])], [gl_cv_func_mbrtoc16_sanitycheck=yes], [gl_cv_func_mbrtoc16_sanitycheck=no], [:]) fi ]) case "$gl_cv_func_mbrtoc16_sanitycheck" in *yes) HAVE_WORKING_MBRTOC16=1 AC_DEFINE([HAVE_WORKING_MBRTOC16], [1], [Define if the mbrtoc16 function basically works.]) ;; *) HAVE_WORKING_MBRTOC16=0 ;; esac fi AC_SUBST([HAVE_WORKING_MBRTOC16]) ]) # Prerequisites of lib/mbrtoc16.c. AC_DEFUN([gl_PREREQ_MBRTOC16], [ : ])