crt: add tests for C95 conversion functions
Signed-off-by: Kirill Makurin <maiddaisuki@outlook.com>
diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 7e7fde5..97da616 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -4403,6 +4403,9 @@
testcases/t_imagebase \
testcases/t_lfs \
testcases/t_matherr \
+ testcases/t_mbrlen \
+ testcases/t_mbrtowc \
+ testcases/t_mbsrtowcs \
testcases/t_nullptrexception \
testcases/t_readdir \
testcases/t_snprintf \
@@ -4430,6 +4433,8 @@
testcases/t_trycatch \
testcases/t_stat_slash \
testcases/t_vsscanf \
+ testcases/t_wcrtomb \
+ testcases/t_wcsrtombs \
testcases/t_wreaddir \
testcases/t_fseeko64
diff --git a/mingw-w64-crt/testcases/t_mbrlen.c b/mingw-w64-crt/testcases/t_mbrlen.c
new file mode 100644
index 0000000..0cf6b18
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_mbrlen.c
@@ -0,0 +1,139 @@
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+char Ascii[] = {'a'};
+char NonAscii[] = {(char) 0x80};
+char Multibyte[] = {(char) 0x81, (char) 0x81};
+char InvalidMultibyte[] = {(char) 0x81, 0};
+
+int main (void) {
+#if __MSVCRT_VERSION__ >= 0x0800
+ return 77;
+#endif
+ mbstate_t state = {0};
+
+ /**
+ * Test "C" locale
+ */
+ assert (setlocale (LC_ALL, "C") != NULL);
+ assert (MB_CUR_MAX == 1);
+
+ /**
+ * All bytes in range [0,255] are valid
+ */
+ for (unsigned char c = 0;; ++c) {
+ assert (mbrlen ((char *) &c, MB_CUR_MAX, &state) == !!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 mbrlen.
+ */
+ state = Ascii[0];
+
+ assert (mbrlen ((char *) &Ascii, MB_CUR_MAX, &state) == (size_t) -1);
+ assert (!mbsinit (&state));
+ assert (errno == EINVAL);
+
+ // reset errno
+ _set_errno (0);
+
+ /**
+ * Set conversion state to initial state
+ */
+
+ assert (mbrlen (NULL, 0, &state) == 0);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Test SBCS code page
+ */
+ assert (setlocale (LC_ALL, "English_United States.ACP") != NULL);
+ assert (MB_CUR_MAX == 1);
+
+ /**
+ * All bytes must be valid
+ */
+ for (unsigned char c = 0;; ++c) {
+ assert (mbrlen ((char *) &c, MB_CUR_MAX, &state) == !!c);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ if (c == 0xFF) {
+ break;
+ }
+ }
+
+ /**
+ * Test DBCS code page
+ */
+ assert (setlocale (LC_ALL, "Japanese_Japan.ACP") != NULL);
+ assert (MB_CUR_MAX == 2);
+
+ /**
+ * Make sure ASCII characters are handled correctly
+ */
+ for (char c = 0;; ++c) {
+ assert (mbrlen (&c, 1, &state) == !!c);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ if (c == 0x7F) {
+ break;
+ }
+ }
+
+ /**
+ * Try convert incomplete multibyte character
+ */
+
+ assert (mbrlen ((char *) Multibyte, 1, &state) == (size_t) -2);
+ assert (!mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Complete multibyte character
+ *
+ * NOTE: return value does not conform to ISO C and POSIX.
+ * This behavior is implemented for consistency with CRT.
+ */
+
+ assert (mbrlen ((char *) Multibyte + 1, 1, &state) == 2);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert multibyte character
+ */
+
+ assert (mbrlen ((char *) Multibyte, MB_CUR_MAX, &state) == 2);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Invalid multibyte character
+ */
+
+ assert (mbrlen ((char *) InvalidMultibyte, MB_CUR_MAX, &state) == (size_t) -1);
+ assert (mbsinit (&state));
+ assert (errno == EILSEQ);
+
+ return 0;
+}
diff --git a/mingw-w64-crt/testcases/t_mbrtowc.c b/mingw-w64-crt/testcases/t_mbrtowc.c
new file mode 100644
index 0000000..fd2fcb7
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_mbrtowc.c
@@ -0,0 +1,164 @@
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+char Ascii[] = {'a'};
+char NonAscii[] = {(char) 0x80};
+char Multibyte[] = {(char) 0x81, (char) 0x81};
+char InvalidMultibyte[] = {(char) 0x81, 0};
+
+int main (void) {
+#if __MSVCRT_VERSION__ >= 0x0800
+ 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.
+ */
+ 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);
+
+ /**
+ * Set conversion state to initial state
+ */
+ wc = WEOF;
+
+ assert (mbrtowc (&wc, NULL, 0, &state) == 0);
+ assert (wc == WEOF);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Test SBCS code page
+ * NOTE: Code page 28951 is ISO-8859-1
+ */
+ assert (setlocale (LC_ALL, "English_United States.28591") != NULL);
+ assert (MB_CUR_MAX == 1);
+
+ /**
+ * All bytes must be valid
+ *
+ * We test ISO-8859-1 so that all bytes must convert to themselves
+ */
+ for (unsigned char c = 0;; ++c) {
+ wc = WEOF;
+
+ assert (mbrtowc (&wc, (char *) &c, MB_CUR_MAX, &state) == !!c);
+ assert (wc == c);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ if (c == 0xFF) {
+ break;
+ }
+ }
+
+ /**
+ * Test DBCS code page
+ */
+ assert (setlocale (LC_ALL, "Japanese_Japan.ACP") != NULL);
+ 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
+ *
+ * NOTE: return value does not conform to ISO C and POSIX.
+ * This behavior is implemented for consistency with CRT.
+ */
+ wc = WEOF;
+
+ assert (mbrtowc (&wc, (char *) Multibyte + 1, 1, &state) == 2);
+ 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);
+
+ return 0;
+}
diff --git a/mingw-w64-crt/testcases/t_mbsrtowcs.c b/mingw-w64-crt/testcases/t_mbsrtowcs.c
new file mode 100644
index 0000000..ba2b838
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_mbsrtowcs.c
@@ -0,0 +1,377 @@
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+/* ASCII text */
+char AsciiText[] = "Simple English string.";
+/* SBCS text (code page 1252) */
+unsigned char SBCSText[] = {'a', 'A', 0xC0, 0xE0, 'e', 'E', 0xC8, 0xE8, 0xCB, 0xEB, 0x0};
+/* DBCS text (code page 932) */
+unsigned char DBCSText[] = {0x93, 0xFA, 0x96, 0x7B, 0x8C, 0xEA, 0x83, 0x65, 0x83, 0x4E, 0x83, 0x58, 0x83, 0x67, 0x0};
+/* Mix of single-byte and double-byte characters */
+unsigned char MixedText[] = {0x93, 0xFA, 'n', 'i', 0x96, 0x7B, 'h', 'o', 'n', 0x8C, 0xEA, 'g', 'o', 0x0};
+/* DBCS text with truncated multibyte character */
+unsigned char BadText[] = {0x93, 0xFA, 0x96, 0x7B, 0x8C, 0x0};
+
+int main (void) {
+#if __MSVCRT_VERSION__ >= 0x0800
+ return 77;
+#endif
+ mbstate_t state = {0};
+ wchar_t buffer[BUFSIZ];
+
+ const char *original_text = NULL;
+ const char *text = NULL;
+ size_t text_length = 0;
+
+ /**
+ * Test "C" locale
+ */
+ assert (setlocale (LC_ALL, "C") != NULL);
+ assert (MB_CUR_MAX == 1);
+
+ /* Test ASCII input */
+
+ original_text = AsciiText;
+ text_length = sizeof AsciiText - 1;
+
+ /**
+ * Get length of converted AsciiString
+ *
+ * - return value must be `text_length`
+ * - value of `text` must not change
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ */
+ text = original_text;
+
+ assert (mbsrtowcs (NULL, &text, 0, &state) == text_length);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert AsciiString
+ *
+ * - return value must be `text_length`
+ * - value of `text` must be NULL
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, BUFSIZ, &state) == text_length);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[text_length] == L'\0');
+
+ /**
+ * Convert 10 characters of AsciiString
+ *
+ * - return value must be 10
+ * - value of `text` must be `original_text + 10`
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must not be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, 10, &state) == 10);
+ assert (text == original_text + 10);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[10] == WEOF);
+
+ /* Test SBCS input */
+
+ original_text = (char *) SBCSText;
+ text_length = sizeof SBCSText - 1;
+
+ /**
+ * Get length of converted SBCSText
+ *
+ * - return value must be `text_length`
+ * - value of `text` must not change
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ */
+ text = original_text;
+
+ assert (mbsrtowcs (NULL, &text, 0, &state) == text_length);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert SBCSText
+ *
+ * - return value must be `text_length`
+ * - value of `text` must be NULL
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, BUFSIZ, &state) == text_length);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[text_length] == L'\0');
+
+ /**
+ * Test SBCS code page
+ */
+ assert (setlocale (LC_ALL, "English_United States.ACP") != NULL);
+ assert (MB_CUR_MAX == 1);
+
+ /* Test SBCS input */
+
+ original_text = (char *) SBCSText;
+ text_length = sizeof SBCSText - 1;
+
+ /**
+ * Get length of converted SBCSText
+ *
+ * - return value must be length of `text_length`
+ * - value of `text` must not change
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ */
+ text = original_text;
+
+ assert (mbsrtowcs (NULL, &text, 0, &state) == text_length);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert SBCSText
+ *
+ * - return value must be `text_length`
+ * - value of `text` must be NULL
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, BUFSIZ, &state) == text_length);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[text_length] == L'\0');
+
+ /**
+ * Convert 8 characters in SBCSText
+ *
+ * - return value must be 8
+ * - value of `text` must be `original_text + 8`
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must not be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, 8, &state) == 8);
+ assert (text == original_text + 8);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[8] == WEOF);
+
+ /**
+ * Test DBCS code page
+ */
+ assert (setlocale (LC_ALL, "Japanese_Japan.ACP") != NULL);
+ assert (MB_CUR_MAX == 2);
+
+ /* Test ASCII input */
+
+ original_text = AsciiText;
+ text_length = sizeof AsciiText - 1;
+
+ /**
+ * Convert AsciiString
+ *
+ * - return value must be `text_length`
+ * - value of `text` must be NULL
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, BUFSIZ, &state) == text_length);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[text_length] == L'\0');
+
+ /* Test DBCS input */
+
+ original_text = (char *) DBCSText;
+ text_length = sizeof DBCSText - 1;
+
+ /**
+ * Get length of converted DBCSText
+ *
+ * - return value must be 7
+ * - value of `text` must not change
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ */
+ text = original_text;
+
+ assert (mbsrtowcs (NULL, &text, 0, &state) == 7);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert 3 multibyte characters in DBCSText
+ *
+ * - return value must be 3
+ * - value of `text` must point to `original_text + 6`
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must not be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, 3, &state) == 3);
+ assert (text == original_text + 6);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[0] != WEOF && buffer[1] != WEOF && buffer[2] != WEOF && buffer[3] == WEOF);
+
+ /**
+ * Convert DBCSText
+ *
+ * - return value must be 7
+ * - value of `text` must be NULL
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, BUFSIZ, &state) == 7);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[7] == L'\0');
+
+ /* Test mixed input */
+
+ original_text = (char *) MixedText;
+
+ /**
+ * Get length of converted MixedText
+ *
+ * - return value must be 10
+ * - value of `text` must not change
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ */
+ text = original_text;
+
+ assert (mbsrtowcs (NULL, &text, 0, &state) == 10);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Converted MixedText
+ *
+ * - return value must be 10
+ * - value of `text` must be NULL
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, BUFSIZ, &state) == 10);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[10] == L'\0');
+
+ /**
+ * Converted 7 multibyte characters in MixedText
+ *
+ * - return value must be 7
+ * - value of `text` must be `original_text + 9`
+ * - `state` must be in the initial state
+ * - value of `errno` must not change
+ * - converted string must not be terminated with '\0'
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, 7, &state) == 7);
+ assert (text == original_text + 9);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[7] == WEOF);
+
+ /* Test bad DBCS input */
+
+ original_text = (char *) BadText;
+
+ /**
+ * Try get length of converted BadText
+ *
+ * - return value must be (size_t)-1
+ * - value of `text` must not change
+ * - `state` must be in the initial state
+ * - value of `errno` must be EILSEQ
+ */
+ text = original_text;
+
+ assert (mbsrtowcs (NULL, &text, 0, &state) == (size_t) -1);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == EILSEQ);
+
+ // reset errno
+ _set_errno (0);
+
+ /**
+ * Try convert BadText
+ *
+ * - return value must be (size_t)-1
+ * - value of `text` must be `original_text + 4`
+ * - `state` must be in the initial state
+ * - value of `errno` must be EILSEQ
+ */
+ wmemset (buffer, WEOF, BUFSIZ);
+ text = original_text;
+
+ assert (mbsrtowcs (buffer, &text, BUFSIZ, &state) == (size_t) -1);
+ assert (text == original_text + 4);
+ assert (mbsinit (&state));
+ assert (errno == EILSEQ);
+ /* This assertion fails with CRT's version */
+ assert (buffer[0] != WEOF && buffer[1] != WEOF && buffer[2] == WEOF);
+
+ return 0;
+}
diff --git a/mingw-w64-crt/testcases/t_wcrtomb.c b/mingw-w64-crt/testcases/t_wcrtomb.c
new file mode 100644
index 0000000..76d8ca0
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_wcrtomb.c
@@ -0,0 +1,199 @@
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+int main (void) {
+#if __MSVCRT_VERSION__ >= 0x0800
+ return 77;
+#endif
+ mbstate_t state = {0};
+
+ /**
+ * Test "C" locale
+ */
+ assert (setlocale (LC_ALL, "C") != NULL);
+ assert (MB_CUR_MAX == 1);
+
+ /**
+ * All values in range [0,255] are valid and must convert to themselves
+ */
+ for (wchar_t wc = 0; wc < 0x100; ++wc) {
+ char c = EOF;
+
+ assert (wcrtomb (&c, wc, &state) == 1);
+ assert ((unsigned char) c == wc);
+ assert (errno == 0);
+ assert (mbsinit (&state));
+ }
+
+ /**
+ * Detect invalid conversion state
+ *
+ * NOTE: this is optional error condition specified in POSIX.
+ * This check fails with CRT's wcrtomb.
+ */
+ state = 1;
+
+ if (1) {
+ char c = EOF;
+
+ assert (wcrtomb (&c, L'\0', &state) == (size_t) -1);
+ assert (c == EOF);
+ assert (errno == EINVAL);
+ assert (!mbsinit (&state));
+
+ // reset errno
+ _set_errno (0);
+ }
+
+ /**
+ * Set conversion state to initial state
+ */
+
+ assert (wcrtomb (NULL, WEOF, &state) == 1);
+ assert (errno == 0);
+ assert (mbsinit (&state));
+
+ /**
+ * Try convert character which cannot be repesented by a single byte
+ */
+ if (1) {
+ char buffer = EOF;
+
+ assert (wcrtomb (&buffer, L'語', &state) == (size_t) -1);
+ assert (buffer == EOF);
+ assert (errno == EILSEQ);
+ assert (mbsinit (&state));
+
+ // reset errno
+ _set_errno (0);
+ }
+
+ /**
+ * Try to convert low and high surrogates
+ */
+ for (wchar_t wc = 0;; ++wc) {
+ if (IS_LOW_SURROGATE (wc) || IS_HIGH_SURROGATE (wc)) {
+ char c = EOF;
+
+ assert (wcrtomb (&c, wc, &state) == (size_t) -1);
+ assert (c == EOF);
+ assert (errno = EILSEQ);
+ assert (mbsinit (&state));
+
+ // reset errno
+ _set_errno (0);
+ }
+
+ if (wc == WEOF) {
+ break;
+ }
+ }
+
+ /**
+ * Test SBCS code page
+ * NOTE: Code page 28951 is ISO-8859-1
+ */
+ assert (setlocale (LC_ALL, "English_United States.28591") != NULL);
+ assert (MB_CUR_MAX == 1);
+
+ /**
+ * All values in range [0,255] must convert to themselves
+ */
+ for (wchar_t wc = 0; wc < 0x100; ++wc) {
+ char c = EOF;
+
+ assert (wcrtomb (&c, wc, &state) == 1);
+ assert ((unsigned char) c == wc);
+ assert (errno == 0);
+ assert (mbsinit (&state));
+ }
+
+ /**
+ * Try to convert low and high surrogates
+ */
+ for (wchar_t wc = 0;; ++wc) {
+ if (IS_LOW_SURROGATE (wc) || IS_HIGH_SURROGATE (wc)) {
+ char c = EOF;
+
+ assert (wcrtomb (&c, wc, &state) == (size_t) -1);
+ /* This assertion fails with CRT's version */
+ assert (c == EOF);
+ assert (errno = EILSEQ);
+ assert (mbsinit (&state));
+
+ // reset errno
+ _set_errno (0);
+ }
+
+ if (wc == WEOF) {
+ break;
+ }
+ }
+
+ /**
+ * Test DBCS code page
+ */
+ assert (setlocale (LC_ALL, "Japanese_Japan.ACP") != NULL);
+ assert (MB_CUR_MAX == 2);
+
+ /**
+ * All values in range [0,127] are valid ASCII characters
+ */
+ for (wchar_t wc = 0; wc < 0x80; ++wc) {
+ char c = EOF;
+
+ assert (wcrtomb (&c, wc, &state) == 1);
+ assert ((unsigned char) c == wc);
+ assert (errno == 0);
+ assert (mbsinit (&state));
+ }
+
+ /**
+ * Try convert multibyte characters
+ */
+ wchar_t DBCS[] = {L'日', L'本', L'語', L'。'};
+
+ for (size_t i = 0; i < _countof (DBCS); ++i) {
+ char buffer[2] = {0};
+
+ assert (wcrtomb (buffer, DBCS[i], &state) == 2);
+ assert (buffer[0] != 0 && buffer[1] != 0);
+ assert (errno == 0);
+ assert (mbsinit (&state));
+ }
+
+ /**
+ * Try to convert low and high surrogates
+ */
+ for (wchar_t wc = 0;; ++wc) {
+ if (IS_LOW_SURROGATE (wc) || IS_HIGH_SURROGATE (wc)) {
+ char c = EOF;
+
+ assert (wcrtomb (&c, wc, &state) == (size_t) -1);
+ /* This assertion fails with CRT's version */
+ assert (c == EOF);
+ assert (errno = EILSEQ);
+ assert (mbsinit (&state));
+
+ // reset errno
+ _set_errno (0);
+ }
+
+ if (wc == WEOF) {
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/mingw-w64-crt/testcases/t_wcsrtombs.c b/mingw-w64-crt/testcases/t_wcsrtombs.c
new file mode 100644
index 0000000..f2c660d
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_wcsrtombs.c
@@ -0,0 +1,570 @@
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+wchar_t AsciiText[] = L"Simple English text.";
+wchar_t SBCSText[] = L"Sömè fÛnnÿ têxt";
+wchar_t DBCSText[] = L"日本語テクスト";
+wchar_t MixedText[] = L"日NI本HON語GO";
+wchar_t BadText[] = {L'テ', L'く', WEOF, L'ト'};
+
+int main (void) {
+#if __MSVCRT_VERSION__ >= 0x0800
+ return 77;
+#endif
+ const wchar_t *original_text = NULL;
+ const wchar_t *text = NULL;
+ size_t text_length = 0;
+
+ char buffer[BUFSIZ];
+ mbstate_t state = {0};
+
+ /**
+ * Test "C" locale
+ */
+ assert (setlocale (LC_ALL, "C") != NULL);
+ assert (MB_CUR_MAX == 1);
+
+ /* Test ASCII input */
+
+ original_text = AsciiText;
+ text_length = _countof (AsciiText) - 1;
+
+ /**
+ * Get length of converted AsciiText
+ *
+ * - return value must be `text_length`
+ * - value of `test` must not change
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == text_length);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert AsciiText
+ *
+ * - return value must be `text_length`
+ * - value of `test` must be NULL
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == text_length);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[text_length] == '\0');
+
+ /**
+ * Convert 10 wide characters in AsciiText
+ *
+ * - return value must be 10
+ * - value of `test` must be `original_text + 10`
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must not be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, 10, &state) == 10);
+ assert (text == original_text + 10);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[10] == EOF);
+
+ /* Test SBCS input */
+
+ original_text = SBCSText;
+ text_length = _countof (SBCSText) - 1;
+
+ /**
+ * Get length of converted SBCSText
+ *
+ * - return value must be 15
+ * - value of `text` must not change
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == text_length);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert SBCSText
+ *
+ * - return value must be 15
+ * - value of `text` must be NULL
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == text_length);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[text_length] == '\0');
+
+ /**
+ * Convert 10 wide characters in SBCSText
+ *
+ * - return value must be 10
+ * - value of `text` must be `original_text + 10`
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must not be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, 10, &state) == 10);
+ assert (text == original_text + 10);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[10] == EOF);
+
+ /* Test DBCS input */
+
+ original_text = DBCSText;
+ text_length = _countof (DBCSText) - 1;
+
+ /**
+ * Try get length of converted DBCSText
+ *
+ * - return value must be (size_t)-1
+ * - value of `text` must not change
+ * - value of `errno` must be EILSEQ
+ * - `state` must be in initial state
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == (size_t) -1);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == EILSEQ);
+ assert (buffer[0] == EOF);
+
+ // reset errno
+ _set_errno (0);
+
+ /**
+ * Try convert DBCSText
+ *
+ * - return value must be (size_t)-1
+ * - value of `text` must not change
+ * - value of `errno` must be EILSEQ
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == (size_t) -1);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == EILSEQ);
+
+ // reset errno
+ _set_errno (0);
+
+ /**
+ * Test SBCS code page
+ */
+ assert (setlocale (LC_ALL, "English_United States.ACP") != NULL);
+ assert (MB_CUR_MAX == 1);
+
+ /* Test ASCII input */
+
+ original_text = AsciiText;
+ text_length = _countof (AsciiText) - 1;
+
+ /**
+ * Get length of converted AsciiText
+ *
+ * - return value must be `text_length`
+ * - value of `test` must not change
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == text_length);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert AsciiText
+ *
+ * - return value must be `text_length`
+ * - value of `test` must be NULL
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == text_length);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[text_length] == '\0');
+
+ /**
+ * Convert 10 wide characters in AsciiText
+ *
+ * - return value must be 10
+ * - value of `test` must be `original_text + 10`
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must not be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, 10, &state) == 10);
+ assert (text == original_text + 10);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[10] == EOF);
+
+ /* Test SBCS input */
+
+ original_text = SBCSText;
+ text_length = _countof (SBCSText) - 1;
+
+ /**
+ * Get length of converted SBCSText
+ *
+ * - return value must be 15
+ * - value of `text` must not change
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == text_length);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert SBCSText
+ *
+ * - return value must be 15
+ * - value of `text` must be NULL
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == text_length);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[text_length] == '\0');
+
+ /**
+ * Convert 10 wide characters in SBCSText
+ *
+ * - return value must be 10
+ * - value of `text` must be `original_text + 10`
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must not be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, 10, &state) == 10);
+ assert (text == original_text + 10);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[10] == EOF);
+
+ /* Test DBCS input */
+
+ original_text = DBCSText;
+ text_length = _countof (DBCSText) - 1;
+
+ /**
+ * Try get length of converted DBCSText
+ *
+ * - return value must be (size_t)-1
+ * - value of `text` must not change
+ * - value of `errno` must be EILSEQ
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == (size_t) -1);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == EILSEQ);
+
+ // reset errno
+ _set_errno (0);
+
+ /**
+ * Try convert DBCSText
+ *
+ * - return value must be (size_t)-1
+ * - value of `text` must not change
+ * - value of `errno` must be EILSEQ
+ * - `state` must be in initial state
+ * - nothing must be written to `buffer`
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == (size_t) -1);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == EILSEQ);
+ /* This assertion fails with CRT's version */
+ assert (buffer[0] == EOF);
+
+ // reset errno
+ _set_errno (0);
+
+ /**
+ * Test DBCS code page
+ */
+ assert (setlocale (LC_ALL, "Japanese_Japan.ACP") != NULL);
+ assert (MB_CUR_MAX == 2);
+
+ /* Test ASCII input */
+
+ original_text = AsciiText;
+ text_length = _countof (AsciiText) - 1;
+
+ /**
+ * Get length of converted AsciiText
+ *
+ * - return value must be `text_length`
+ * - value of `test` must not change
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == text_length);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert AsciiText
+ *
+ * - return value must be `text_length`
+ * - value of `test` must be NULL
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == text_length);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[text_length] == '\0');
+
+ /**
+ * Convert 10 wide characters in AsciiText
+ *
+ * - return value must be 10
+ * - value of `test` must be `original_text + 10`
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must not be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, 10, &state) == 10);
+ assert (text == original_text + 10);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[10] == EOF);
+
+ /* Test DBCS input */
+
+ original_text = DBCSText;
+ text_length = _countof (DBCSText) - 1;
+
+ /**
+ * Get length of converted DBCSText
+ *
+ * - return value must be 14
+ * - value of `text` must not change
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == 14);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert DBCSText
+ *
+ * - return value must be 14
+ * - value of `text` must be NULL
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == 14);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[14] == '\0');
+
+ /**
+ * Convert 5 wide characters in DBCSText
+ *
+ * - return value must be 10
+ * - value of `text` must be `original_text + 5`
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must not be terminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, 11, &state) == 10);
+ assert (text == original_text + 5);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[10] == EOF);
+
+ /* Test mixed input */
+
+ original_text = MixedText;
+ text_length = _countof (MixedText) - 1;
+
+ /**
+ * Get length of converted MixedText
+ *
+ * - return value must be 13
+ * - value of `test` must not change
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == 13);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+
+ /**
+ * Convert MixedText
+ *
+ * - return value must be 13
+ * - value of `test` must be NULL
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must be teminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == 13);
+ assert (text == NULL);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[13] == '\0');
+
+ /**
+ * Convert 7 wide characters in MixedText
+ *
+ * - return value must be 9
+ * - value of `test` must be `original_text + 7`
+ * - value of `errno` must not change
+ * - `state` must be in initial state
+ * - converted string must not be teminated with '\0'
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, 10, &state) == 9);
+ assert (text == original_text + 7);
+ assert (mbsinit (&state));
+ assert (errno == 0);
+ assert (buffer[9] == EOF);
+
+ /* Test bad input */
+
+ original_text = BadText;
+ text_length = _countof (BadText) - 1;
+
+ /**
+ * Try get length of converted BadText
+ *
+ * - return value must be (size_t)-1
+ * - value of `text` must not change
+ * - value of `errno` must be EILSEQ
+ * - `state` must be in initial state
+ */
+ text = original_text;
+
+ assert (wcsrtombs (NULL, &text, 0, &state) == (size_t) -1);
+ assert (text == original_text);
+ assert (mbsinit (&state));
+ assert (errno == EILSEQ);
+
+ /**
+ * Try convert BadText
+ *
+ * - return value must be (size_t)-1
+ * - value of `text` must be `original_text + 2`
+ * - value of `errno` must be EILSEQ
+ * - `state` must be in initial state
+ */
+ memset (buffer, EOF, BUFSIZ);
+ text = original_text;
+
+ assert (wcsrtombs (buffer, &text, BUFSIZ, &state) == (size_t) -1);
+ assert (text == original_text + 2);
+ assert (mbsinit (&state));
+ assert (errno == EILSEQ);
+ /* This assertion fails with CRT's version */
+ assert (buffer[3] != EOF && buffer[4] == EOF);
+
+ // reset errno
+ _set_errno (0);
+
+ return 0;
+}