| #ifdef NDEBUG |
| #undef NDEBUG |
| #endif |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <locale.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <wchar.h> |
| |
| /* mingw-w64 headers */ |
| #include "libtest.h" |
| |
| /** |
| * This test is for mingw-w64's implementation of mbrtowc function. |
| * |
| * This implementation is used with all msvcr*.dll CRTs, but not UCRT. |
| * This test is skipped for UCRT. |
| * |
| * Also note that mingw-w64's implementation only works with SBCS and DBCS |
| * code pages (MB_CUR_MAX == 1 || MB_CUR_MAX == 2). |
| */ |
| |
| static void set_conversion_state (mbstate_t *state, int bytes) { |
| #ifdef _UCRT |
| state->_Wchar = bytes; |
| #else |
| *state = bytes; |
| #endif |
| } |
| |
| char Ascii[] = {'a'}; |
| char NonAscii[] = {(char) 0x80}; |
| char Multibyte[] = {(char) 0x81, (char) 0x81}; |
| char InvalidMultibyte[] = {(char) 0x81, 0}; |
| |
| int main (void) { |
| mingw_test_init (); |
| |
| #ifdef _UCRT |
| return 77; |
| #endif |
| |
| mbstate_t state = {0}; |
| wchar_t wc = WEOF; |
| |
| /** |
| * Test "C" locale |
| */ |
| assert (setlocale (LC_ALL, "C") != NULL); |
| assert (MB_CUR_MAX == 1); |
| |
| /** |
| * All bytes in range [0,255] are valid and must convert to themselves |
| */ |
| for (unsigned char c = 0;; ++c) { |
| assert (mbrtowc (&wc, (char *) &c, MB_CUR_MAX, &state) == !!c); |
| assert (wc == c); |
| assert (mbsinit (&state)); |
| assert (errno == 0); |
| |
| if (c == 0xFF) { |
| break; |
| } |
| } |
| |
| /** |
| * Detect invalid conversion state |
| * |
| * NOTE: this is optional error condition specified in POSIX. |
| * This check fails with CRT's mbrtowc. |
| */ |
| set_conversion_state (&state, Ascii[0]); |
| wc = WEOF; |
| |
| assert (mbrtowc (&wc, (char *) &Ascii, MB_CUR_MAX, &state) == (size_t) -1); |
| assert (wc == WEOF); |
| assert (!mbsinit (&state)); |
| assert (errno == EINVAL); |
| |
| // reset errno |
| _set_errno (0); |
| |
| // reset `state` |
| set_conversion_state (&state, 0); |
| |
| /** |
| * Test SBCS code page |
| */ |
| if (setlocale (LC_ALL, ".1252") == NULL) { |
| wprintf (L"Skipping tests for SBCS code page 1252\n"); |
| goto skip_sbcs; |
| } |
| |
| assert (MB_CUR_MAX == 1); |
| |
| /** |
| * All bytes in range [0,127] are valid ASCII characters |
| */ |
| for (unsigned char c = 0; c < 0x80; ++c) { |
| wc = WEOF; |
| |
| assert (mbrtowc (&wc, (char *) &c, MB_CUR_MAX, &state) == !!c); |
| assert (wc == c); |
| assert (mbsinit (&state)); |
| assert (errno == 0); |
| } |
| |
| /** |
| * All bytes in range [128,159] are valid characters |
| */ |
| for (unsigned char c = 0x80; c < 0xA0; ++c) { |
| wc = WEOF; |
| |
| assert (mbrtowc (&wc, (char *) &c, MB_CUR_MAX, &state) == 1); |
| if (c == 0x81 || c == 0x8D || c == 0x8F || c == 0x90 || c == 0x9D) { |
| assert (wc == c); |
| } else { |
| assert (wc != WEOF); |
| assert (wc != c); |
| } |
| assert (mbsinit (&state)); |
| assert (errno == 0); |
| } |
| |
| /** |
| * All bytes in range [160,255] are valid and must convert to themselves |
| */ |
| for (unsigned char c = 0xA0;; ++c) { |
| wc = WEOF; |
| |
| assert (mbrtowc (&wc, (char *) &c, MB_CUR_MAX, &state) == 1); |
| assert (wc == c); |
| assert (mbsinit (&state)); |
| assert (errno == 0); |
| |
| if (c == 0xFF) { |
| break; |
| } |
| } |
| |
| skip_sbcs: |
| /** |
| * Disable tests for DBCS code pages with msvcrt10.dll since it does not |
| * support multibyte characters. |
| * |
| * Calling setlocale with locale string which requests DBCS code page |
| * result in runtime error. |
| */ |
| #if __MSVCRT_VERSION__ != 0x0100 |
| /** |
| * Test DBCS code page |
| */ |
| if (setlocale (LC_ALL, ".932") == NULL) { |
| wprintf (L"Skipping tests for DBCS code page 932\n"); |
| goto skip_dbcs; |
| } |
| |
| assert (MB_CUR_MAX == 2); |
| |
| /** |
| * Make sure ASCII characters are handled correctly |
| */ |
| for (char c = 0;; ++c) { |
| wc = WEOF; |
| |
| assert (mbrtowc (&wc, &c, 1, &state) == !!c); |
| assert (wc == c); |
| assert (mbsinit (&state)); |
| assert (errno == 0); |
| |
| if (c == 0x7F) { |
| break; |
| } |
| } |
| |
| /** |
| * Try convert incomplete multibyte character |
| */ |
| wc = WEOF; |
| |
| assert (mbrtowc (&wc, (char *) Multibyte, 1, &state) == (size_t) -2); |
| /* This assertion fails with CRT's version */ |
| assert (wc == WEOF); |
| assert (!mbsinit (&state)); |
| assert (errno == 0); |
| |
| /** |
| * Complete multibyte character |
| */ |
| wc = WEOF; |
| |
| assert (mbrtowc (&wc, (char *) Multibyte + 1, 1, &state) == 1); |
| assert (wc != WEOF); |
| assert (mbsinit (&state)); |
| assert (errno == 0); |
| |
| /** |
| * Convert complete multibyte character |
| */ |
| wc = WEOF; |
| |
| assert (mbrtowc (&wc, (char *) Multibyte, MB_CUR_MAX, &state) == 2); |
| assert (wc != WEOF); |
| assert (mbsinit (&state)); |
| assert (errno == 0); |
| |
| /** |
| * Try convert invalid multibyte character |
| */ |
| wc = WEOF; |
| |
| assert (mbrtowc (&wc, (char *) InvalidMultibyte, MB_CUR_MAX, &state) == (size_t) -1); |
| /* This assertion fails with CRT's version */ |
| assert (wc == WEOF); |
| assert (mbsinit (&state)); |
| assert (errno == EILSEQ); |
| |
| skip_dbcs: |
| #endif |
| return 0; |
| } |