16 #define SETLOCALE(locale) \ 17 loc = setlocale(LC_ALL, locale); \ 20 puts("setlocale failed for " locale ", this locale is probably not installed on your system"); \ 24 #define OK(condition, fail_message, ...) \ 26 printf("%d: " fail_message "\n", __LINE__, ##__VA_ARGS__); 34 wchar_t wcs[5] = {
'T',
'h', 1088,
'i', 0};
35 wchar_t dbwcs[3] = {28953, 25152, 0};
51 OK(
mbs[0] == 0,
"mbs[0] is %d",
mbs[0]);
165 OK(!
strcmp(
mbs,
"µH©Ò ), "mbs is %s", mbs);
ZeroMemory(mbs, 5);
/* Length-only tests */
SETLOCALE("English");
ret = wcstombs(NULL, wcs, 0);
OK(ret == -1, "ret is %d", ret);
OK(errno == EILSEQ, "errno is %d", errno);
SETLOCALE("Chinese");
ret = wcstombs(NULL, dbwcs, 0);
OK(ret == 4, "ret is %d", ret);
/* This call causes an ERROR_INSUFFICIENT_BUFFER in the called WideCharToMultiByte function.
For some reason, wcstombs under Windows doesn't reset the last error to the previous value here, so we can check for ERROR_INSUFFICIENT_BUFFER with GetLastError().
This could also be seen as an indication that Windows uses WideCharToMultiByte internally for wcstombs. */
ret = wcstombs(mbs, dbwcs, 1);
OK(ret == 0, "ret is %d", ret);
OK(mbs[0] == 0, "mbs[0] is %d", mbs[0]);
/* ERROR_INSUFFICIENT_BUFFER is also the result of this call with SBCS characters. WTF?!
Anyway this is a Win32 error not related to the CRT, so we leave out this criteria. */
ret = wcstombs(mbs, wcs, 1);
OK(ret == 1, "ret is %d", ret);
OK(mbs[0] == 84, "mbs[0] is %d", mbs[0]);
putchar('\n');
}
void Win32_Tests(LPBOOL bUsedDefaultChar)
{
SetLastError(0xdeadbeef);
puts("Win32-Tests");
puts("-----------");
ret = WideCharToMultiByte(1252, 0, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 1, "ret is %d", ret);
OK(mbc == -28, "mbc is %d", mbc);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(1252, 0, &wc2, 1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 1, "ret is %d", ret);
OK(mbc == 63, "mbc is %d", mbc);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(1251, 0, &wc2, 1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 1, "ret is %d", ret);
OK(mbc == -16, "mbc is %d", mbc);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(1251, 0, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 1, "ret is %d", ret);
OK(mbc == 97, "mbc is %d", mbc);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
/* The behaviour for this character is different when WC_NO_BEST_FIT_CHARS is used */
ret = WideCharToMultiByte(1251, WC_NO_BEST_FIT_CHARS, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 1, "ret is %d", ret);
OK(mbc == 63, "mbc is %d", mbc);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(1252, 0, dbwcs, -1, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 3, "ret is %d", ret);
OK(!strcmp(mbs, "??"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ZeroMemory(mbs, 5);
ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, dbwcs, -1, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 3, "ret is %d", ret);
OK(!strcmp(mbs, "??"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
/* This call triggers the last Win32 error */
ret = WideCharToMultiByte(1252, 0, wcs, -1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 0, "ret is %d", ret);
OK(mbc == 84, "mbc is %d", mbc);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetLastError() is %lu", GetLastError());
SetLastError(0xdeadbeef);
ret = WideCharToMultiByte(1252, 0, wcs, -1, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 5, "ret is %d", ret);
OK(!strcmp(mbs, "Th?i"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
mbs[0] = 0;
/* WideCharToMultiByte mustn't add any null character automatically.
So in this case, we should get the same string again, even if we only copied the first three bytes. */
ret = WideCharToMultiByte(1252, 0, wcs, 3, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 3, "ret is %d", ret);
OK(!strcmp(mbs, "Th?i"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ZeroMemory(mbs, 5);
/* Now this shouldn't be the case like above as we zeroed the complete string buffer. */
ret = WideCharToMultiByte(1252, 0, wcs, 3, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 3, "ret is %d", ret);
OK(!strcmp(mbs, "Th?"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
/* Chinese codepage tests
Swapping the WC_NO_BEST_FIT_CHARS and 0 tests causes bUsedDefaultChar to be set to TRUE in the following test, which quits with ERROR_INSUFFICIENT_BUFFER.
But as it isn't documented whether all other variables are undefined if ERROR_INSUFFICIENT_BUFFER is set, we skip this behaviour. */
ret = WideCharToMultiByte(950, WC_NO_BEST_FIT_CHARS, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 1, "ret is %d", ret);
OK(mbc == 63, "mbc is %d", mbc);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(950, 0, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 1, "ret is %d", ret);
OK(mbc == 97, "mbc is %d", mbc);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
/* Double-byte tests */
ret = WideCharToMultiByte(950, 0, dbwcs, -1, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 5, "ret is %d", ret);
OK(!strcmp(mbs, "µH©Ò"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(950, 0, dbwcs, 1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 0, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetLastError() is %lu", GetLastError());
SetLastError(0xdeadbeef);
ZeroMemory(mbs, 5);
ret = WideCharToMultiByte(950, 0, dbwcs, 1, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 2, "ret is %d", ret);
OK(!strcmp(mbs, "µH"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
/* Length-only tests */
ret = WideCharToMultiByte(1252, 0, &wc2, 1, NULL, 0, NULL, bUsedDefaultChar);
OK(ret == 1, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(1252, 0, wcs, -1, NULL, 0, NULL, bUsedDefaultChar);
OK(ret == 5, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(950, 0, dbwcs, 1, NULL, 0, NULL, bUsedDefaultChar);
OK(ret == 2, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(950, 0, dbwcs, -1, NULL, 0, NULL, bUsedDefaultChar);
OK(ret == 5, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
/* Abnormal uses of WideCharToMultiByte */
ret = WideCharToMultiByte(1252, 0, NULL, 5, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 0, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError() is %lu", GetLastError());
SetLastError(0xdeadbeef);
ret = WideCharToMultiByte(0, 0, dbwcs, 5, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 5, "ret is %d", ret);
OK(!strcmp(mbs, "??"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
ret = WideCharToMultiByte(1252, 0, wcs, -1, (LPSTR)wcs, 5, NULL, bUsedDefaultChar);
OK(ret == 0, "ret is %d", ret);
OK(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError() is %lu", GetLastError());
SetLastError(0xdeadbeef);
ret = WideCharToMultiByte(1252, 0, wcs, -1, mbs, -1, NULL, bUsedDefaultChar);
OK(ret == 0, "ret is %d", ret);
OK(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError() is %lu", GetLastError());
SetLastError(0xdeadbeef);
putchar('\n');
}
int main()
{
BOOL UsedDefaultChar;
CRT_Tests();
/* There are two code pathes in WideCharToMultiByte, one when Flags || DefaultChar || UsedDefaultChar is set and one when it's not.
Test both here. */
Win32_Tests(NULL);
Win32_Tests(&UsedDefaultChar);
return 0;
}
"),
"mbs is %s",
mbs);
183 OK(
mbs[0] == 0,
"mbs[0] is %d",
mbs[0]);
189 OK(
mbs[0] == 84,
"mbs[0] is %d",
mbs[0]);
204 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
210 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
216 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
222 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
229 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
235 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
242 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
249 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
256 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
265 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
273 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
282 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
288 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
294 OK(!
strcmp(
mbs,
"µH©Ò ), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(950, 0, dbwcs, 1, &mbc, 1, NULL, bUsedDefaultChar);
OK(ret == 0, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetLastError() is %lu", GetLastError());
SetLastError(0xdeadbeef);
ZeroMemory(mbs, 5);
ret = WideCharToMultiByte(950, 0, dbwcs, 1, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 2, "ret is %d", ret);
OK(!strcmp(mbs, "µH"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
/* Length-only tests */
ret = WideCharToMultiByte(1252, 0, &wc2, 1, NULL, 0, NULL, bUsedDefaultChar);
OK(ret == 1, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(1252, 0, wcs, -1, NULL, 0, NULL, bUsedDefaultChar);
OK(ret == 5, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(950, 0, dbwcs, 1, NULL, 0, NULL, bUsedDefaultChar);
OK(ret == 2, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
ret = WideCharToMultiByte(950, 0, dbwcs, -1, NULL, 0, NULL, bUsedDefaultChar);
OK(ret == 5, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
/* Abnormal uses of WideCharToMultiByte */
ret = WideCharToMultiByte(1252, 0, NULL, 5, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 0, "ret is %d", ret);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
OK(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError() is %lu", GetLastError());
SetLastError(0xdeadbeef);
ret = WideCharToMultiByte(0, 0, dbwcs, 5, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
OK(ret == 5, "ret is %d", ret);
OK(!strcmp(mbs, "??"), "mbs is %s", mbs);
if(bUsedDefaultChar) OK(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d", *bUsedDefaultChar);
ret = WideCharToMultiByte(1252, 0, wcs, -1, (LPSTR)wcs, 5, NULL, bUsedDefaultChar);
OK(ret == 0, "ret is %d", ret);
OK(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError() is %lu", GetLastError());
SetLastError(0xdeadbeef);
ret = WideCharToMultiByte(1252, 0, wcs, -1, mbs, -1, NULL, bUsedDefaultChar);
OK(ret == 0, "ret is %d", ret);
OK(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError() is %lu", GetLastError());
SetLastError(0xdeadbeef);
putchar('\n');
}
int main()
{
BOOL UsedDefaultChar;
CRT_Tests();
/* There are two code pathes in WideCharToMultiByte, one when Flags || DefaultChar || UsedDefaultChar is set and one when it's not.
Test both here. */
Win32_Tests(NULL);
Win32_Tests(&UsedDefaultChar);
return 0;
}
"),
"mbs is %s",
mbs);
295 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
300 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
308 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
314 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
319 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
324 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
329 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
335 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
FALSE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
342 if(bUsedDefaultChar)
OK(*bUsedDefaultChar ==
TRUE,
"bUsedDefaultChar is %d", *bUsedDefaultChar);
359 BOOL UsedDefaultChar;
#define ERROR_INVALID_PARAMETER
#define OK(condition, fail_message,...)
#define WideCharToMultiByte
DWORD WINAPI GetLastError(VOID)
void Win32_Tests(LPBOOL bUsedDefaultChar)
#define WC_NO_BEST_FIT_CHARS
int puts(const char *string)
int strcmp(const char *String1, const char *String2)
size_t __cdecl wcstombs(_Out_writes_opt_z_(_MaxCount) char *_Dest, _In_z_ const wchar_t *_Source, _In_ size_t _MaxCount)
#define SETLOCALE(locale)
#define ERROR_INSUFFICIENT_BUFFER