Reactos
at master 1379 lines 55 kB view raw
1/* 2 * Unit tests for code page to/from unicode translations 3 * 4 * Copyright (c) 2002 Dmitry Timoshkov 5 * Copyright (c) 2008 Colin Finck 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22#include <stdarg.h> 23#include <stdio.h> 24#include <limits.h> 25 26#include "wine/test.h" 27#include "windef.h" 28#include "winbase.h" 29#include "winnls.h" 30 31static const char foobarA[] = "foobar"; 32static const WCHAR foobarW[] = {'f','o','o','b','a','r',0}; 33 34static void test_destination_buffer(void) 35{ 36 LPSTR buffer; 37 INT maxsize; 38 INT needed; 39 INT len; 40 41 SetLastError(0xdeadbeef); 42 needed = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, NULL, 0, NULL, NULL); 43 ok( (needed > 0), "returned %d with %lu (expected '> 0')\n", 44 needed, GetLastError()); 45 46 maxsize = needed*2; 47 buffer = HeapAlloc(GetProcessHeap(), 0, maxsize); 48 if (buffer == NULL) return; 49 50 maxsize--; 51 memset(buffer, 'x', maxsize); 52 buffer[maxsize] = '\0'; 53 SetLastError(0xdeadbeef); 54 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, needed+1, NULL, NULL); 55 ok( (len > 0), "returned %d with %lu and '%s' (expected '> 0')\n", 56 len, GetLastError(), buffer); 57 ok(!lstrcmpA(buffer, "foobar"), "expected \"foobar\" got \"%s\"\n", buffer); 58 59 memset(buffer, 'x', maxsize); 60 buffer[maxsize] = '\0'; 61 SetLastError(0xdeadbeef); 62 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, needed, NULL, NULL); 63 ok( (len > 0), "returned %d with %lu and '%s' (expected '> 0')\n", 64 len, GetLastError(), buffer); 65 ok(!lstrcmpA(buffer, "foobar"), "expected \"foobar\" got \"%s\"\n", buffer); 66 67 memset(buffer, 'x', maxsize); 68 buffer[maxsize] = '\0'; 69 SetLastError(0xdeadbeef); 70 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, needed-1, NULL, NULL); 71 ok( !len && (GetLastError() == ERROR_INSUFFICIENT_BUFFER), 72 "returned %d with %lu and '%s' (expected '0' with " 73 "ERROR_INSUFFICIENT_BUFFER)\n", len, GetLastError(), buffer); 74 ok(!strncmp(buffer, "foobar", 6), "expected \"foobar\" got \"%s\"\n", buffer); 75 ok(buffer[6] == 'x', "expected buf[5]=='x', got \"%s\"\n", buffer); 76 77 memset(buffer, 'x', maxsize); 78 buffer[maxsize] = '\0'; 79 SetLastError(0xdeadbeef); 80 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, 1, NULL, NULL); 81 ok( !len && (GetLastError() == ERROR_INSUFFICIENT_BUFFER), 82 "returned %d with %lu and '%s' (expected '0' with " 83 "ERROR_INSUFFICIENT_BUFFER)\n", len, GetLastError(), buffer); 84 ok(buffer[0] == 'f', "expected buf[1]=='f', got \"%s\"\n", buffer); 85 ok(buffer[1] == 'x', "expected buf[1]=='x', got \"%s\"\n", buffer); 86 87 SetLastError(0xdeadbeef); 88 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, 0, NULL, NULL); 89 ok( (len > 0), "returned %d with %lu (expected '> 0')\n", 90 len, GetLastError()); 91 92 SetLastError(0xdeadbeef); 93 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, NULL, needed, NULL, NULL); 94 ok( !len && (GetLastError() == ERROR_INVALID_PARAMETER), 95 "returned %d with %lu (expected '0' with " 96 "ERROR_INVALID_PARAMETER)\n", len, GetLastError()); 97 98 HeapFree(GetProcessHeap(), 0, buffer); 99} 100 101 102static void test_null_source(void) 103{ 104 int len; 105 DWORD GLE; 106 107 SetLastError(0); 108 len = WideCharToMultiByte(CP_ACP, 0, NULL, 0, NULL, 0, NULL, NULL); 109 GLE = GetLastError(); 110 ok(!len && GLE == ERROR_INVALID_PARAMETER, 111 "WideCharToMultiByte returned %d with GLE=%lu (expected 0 with ERROR_INVALID_PARAMETER)\n", 112 len, GLE); 113 114 SetLastError(0); 115 len = WideCharToMultiByte(CP_ACP, 0, NULL, -1, NULL, 0, NULL, NULL); 116 GLE = GetLastError(); 117 ok(!len && GLE == ERROR_INVALID_PARAMETER, 118 "WideCharToMultiByte returned %d with GLE=%lu (expected 0 with ERROR_INVALID_PARAMETER)\n", 119 len, GLE); 120} 121 122static void test_negative_source_length(void) 123{ 124 int len; 125 char buf[10]; 126 WCHAR bufW[10]; 127 128 /* Test, whether any negative source length works as strlen() + 1 */ 129 SetLastError( 0xdeadbeef ); 130 memset(buf,'x',sizeof(buf)); 131 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -2002, buf, 10, NULL, NULL); 132 ok(len == 7 && GetLastError() == 0xdeadbeef, 133 "WideCharToMultiByte(-2002): len=%d error=%lu\n", len, GetLastError()); 134 ok(!lstrcmpA(buf, "foobar"), 135 "WideCharToMultiByte(-2002): expected \"foobar\" got \"%s\"\n", buf); 136 137 SetLastError( 0xdeadbeef ); 138 memset(bufW,'x',sizeof(bufW)); 139 len = MultiByteToWideChar(CP_ACP, 0, "foobar", -2002, bufW, 10); 140 ok(len == 7 && !lstrcmpW(bufW, foobarW) && GetLastError() == 0xdeadbeef, 141 "MultiByteToWideChar(-2002): len=%d error=%lu\n", len, GetLastError()); 142 143 SetLastError(0xdeadbeef); 144 memset(bufW, 'x', sizeof(bufW)); 145 len = MultiByteToWideChar(CP_ACP, 0, "foobar", -1, bufW, 6); 146 ok(len == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER, 147 "MultiByteToWideChar(-1): len=%d error=%lu\n", len, GetLastError()); 148} 149 150#define LONGBUFLEN 100000 151static void test_negative_dest_length(void) 152{ 153 int len, i; 154 static WCHAR bufW[LONGBUFLEN]; 155 static char bufA[LONGBUFLEN]; 156 static WCHAR originalW[LONGBUFLEN]; 157 static char originalA[LONGBUFLEN]; 158 DWORD theError; 159 160 /* Test return on -1 dest length */ 161 SetLastError( 0xdeadbeef ); 162 memset(bufA,'x',sizeof(bufA)); 163 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, bufA, -1, NULL, NULL); 164 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, 165 "WideCharToMultiByte(destlen -1): len=%d error=%lx\n", len, GetLastError()); 166 167 SetLastError( 0xdeadbeef ); 168 memset(bufW,'x',sizeof(bufW)); 169 len = MultiByteToWideChar(CP_ACP, 0, foobarA, -1, bufW, -1); 170 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, 171 "MultiByteToWideChar(destlen -1): len=%d error=%lx\n", len, GetLastError()); 172 173 /* Test return on -1000 dest length */ 174 SetLastError( 0xdeadbeef ); 175 memset(bufA,'x',sizeof(bufA)); 176 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, bufA, -1000, NULL, NULL); 177 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, 178 "WideCharToMultiByte(destlen -1000): len=%d error=%lx\n", len, GetLastError()); 179 180 SetLastError( 0xdeadbeef ); 181 memset(bufW,'x',sizeof(bufW)); 182 len = MultiByteToWideChar(CP_ACP, 0, foobarA, -1000, bufW, -1); 183 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, 184 "MultiByteToWideChar(destlen -1000): len=%d error=%lx\n", len, GetLastError()); 185 186 /* Test return on INT_MAX dest length */ 187 SetLastError( 0xdeadbeef ); 188 memset(bufA,'x',sizeof(bufA)); 189 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, bufA, INT_MAX, NULL, NULL); 190 ok(len == 7 && !lstrcmpA(bufA, "foobar") && GetLastError() == 0xdeadbeef, 191 "WideCharToMultiByte(destlen INT_MAX): len=%d error=%lx\n", len, GetLastError()); 192 193 /* Test return on INT_MAX dest length and very long input */ 194 SetLastError( 0xdeadbeef ); 195 memset(bufA,'x',sizeof(bufA)); 196 for (i=0; i < LONGBUFLEN - 1; i++) { 197 originalW[i] = 'Q'; 198 originalA[i] = 'Q'; 199 } 200 originalW[LONGBUFLEN-1] = 0; 201 originalA[LONGBUFLEN-1] = 0; 202 len = WideCharToMultiByte(CP_ACP, 0, originalW, -1, bufA, INT_MAX, NULL, NULL); 203 theError = GetLastError(); 204 ok(len == LONGBUFLEN && !lstrcmpA(bufA, originalA) && theError == 0xdeadbeef, 205 "WideCharToMultiByte(srclen %d, destlen INT_MAX): len %d error=%lx\n", LONGBUFLEN, len, theError); 206 207} 208 209static void test_other_invalid_parameters(void) 210{ 211 char c_string[] = "Hello World"; 212 size_t c_string_len = sizeof(c_string); 213 WCHAR w_string[] = {'H','e','l','l','o',' ','W','o','r','l','d',0}; 214 size_t w_string_len = ARRAY_SIZE(w_string); 215 BOOL used; 216 INT len; 217 218 /* Unrecognized flag => ERROR_INVALID_FLAGS */ 219 SetLastError(0xdeadbeef); 220 len = WideCharToMultiByte(CP_ACP, 0x100, w_string, -1, c_string, c_string_len, NULL, NULL); 221 ok(len == 0 && GetLastError() == ERROR_INVALID_FLAGS, "len=%d error=%lx\n", len, GetLastError()); 222 223 SetLastError(0xdeadbeef); 224 len = WideCharToMultiByte(CP_ACP, 0x800, w_string, -1, c_string, c_string_len, NULL, NULL); 225 ok(len == 0 && GetLastError() == ERROR_INVALID_FLAGS, "len=%d error=%lx\n", len, GetLastError()); 226 227 SetLastError(0xdeadbeef); 228 len = MultiByteToWideChar(CP_ACP, 0x10, c_string, -1, w_string, w_string_len); 229 ok(len == 0 && GetLastError() == ERROR_INVALID_FLAGS, "len=%d error=%lx\n", len, GetLastError()); 230 231 232 /* Unrecognized flag and invalid codepage => ERROR_INVALID_PARAMETER */ 233 SetLastError(0xdeadbeef); 234 len = WideCharToMultiByte(0xdeadbeef, 0x100, w_string, w_string_len, c_string, c_string_len, NULL, NULL); 235 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 236 237 SetLastError(0xdeadbeef); 238 len = MultiByteToWideChar(0xdeadbeef, 0x10, c_string, c_string_len, w_string, w_string_len); 239 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 240 241 242 /* Unrecognized flag and src is NULL => ERROR_INVALID_PARAMETER */ 243 SetLastError(0xdeadbeef); 244 len = WideCharToMultiByte(CP_ACP, 0x100, NULL, -1, c_string, c_string_len, NULL, NULL); 245 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 246 247 SetLastError(0xdeadbeef); 248 len = MultiByteToWideChar(CP_ACP, 0x10, NULL, -1, w_string, w_string_len); 249 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 250 251 252 /* srclen=0 => ERROR_INVALID_PARAMETER */ 253 SetLastError(0xdeadbeef); 254 len = WideCharToMultiByte(CP_ACP, 0, w_string, 0, c_string, c_string_len, NULL, NULL); 255 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 256 257 SetLastError(0xdeadbeef); 258 len = MultiByteToWideChar(CP_ACP, 0, c_string, 0, w_string, w_string_len); 259 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 260 261 262 /* dst=NULL but dstlen not 0 => ERROR_INVALID_PARAMETER */ 263 SetLastError(0xdeadbeef); 264 len = WideCharToMultiByte(CP_ACP, 0, w_string, w_string_len, NULL, c_string_len, NULL, NULL); 265 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 266 267 SetLastError(0xdeadbeef); 268 len = MultiByteToWideChar(CP_ACP, 0, c_string, c_string_len, NULL, w_string_len); 269 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 270 271 272 /* CP_UTF7 or CP_SYMBOL and defchar not NULL => ERROR_INVALID_PARAMETER */ 273 /* CP_UTF8 allows it since win10 1709 */ 274 SetLastError(0xdeadbeef); 275 len = WideCharToMultiByte(CP_UTF7, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL); 276 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 277 278 SetLastError(0xdeadbeef); 279 len = WideCharToMultiByte(CP_UTF8, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL); 280 ok(len == 12 || broken(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER), 281 "len=%d error=%lx\n", len, GetLastError()); 282 283 SetLastError(0xdeadbeef); 284 len = WideCharToMultiByte(CP_SYMBOL, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL); 285 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 286 287 288 /* CP_UTF7 or CP_SYMBOL and used not NULL => ERROR_INVALID_PARAMETER */ 289 /* CP_UTF8 allows it since win10 1709 */ 290 SetLastError(0xdeadbeef); 291 len = WideCharToMultiByte(CP_UTF7, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used); 292 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 293 294 SetLastError(0xdeadbeef); 295 len = WideCharToMultiByte(CP_UTF8, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used); 296 ok(len == 12 || broken(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER), 297 "len=%d error=%lx\n", len, GetLastError()); 298 299 SetLastError(0xdeadbeef); 300 len = WideCharToMultiByte(CP_SYMBOL, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used); 301 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 302 303 304 /* CP_UTF7, flags not 0 and used not NULL => ERROR_INVALID_PARAMETER */ 305 /* (tests precedence of ERROR_INVALID_PARAMETER over ERROR_INVALID_FLAGS) */ 306 /* The same test with CP_SYMBOL instead of CP_UTF7 gives ERROR_INVALID_FLAGS 307 instead except on Windows NT4 */ 308 SetLastError(0xdeadbeef); 309 len = WideCharToMultiByte(CP_UTF7, 1, w_string, w_string_len, c_string, c_string_len, NULL, &used); 310 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%lx\n", len, GetLastError()); 311 312 /* CP_UTF8, unrecognized flag and used not NULL => ERROR_INVALID_PARAMETER */ 313 SetLastError(0xdeadbeef); 314 len = WideCharToMultiByte(CP_UTF8, 0x100, w_string, w_string_len, c_string, c_string_len, NULL, &used); 315 ok(len == 0, "wrong ret %d\n", len); 316 ok(GetLastError() == ERROR_INVALID_PARAMETER 317 || GetLastError() == ERROR_INVALID_FLAGS /* Win10 1709+ */, "wrong error %lu\n", GetLastError()); 318} 319 320static void test_overlapped_buffers(void) 321{ 322 static const WCHAR strW[] = {'j','u','s','t',' ','a',' ','t','e','s','t',0}; 323 static const char strA[] = "just a test"; 324 char buf[256]; 325 int ret; 326 327 /* limit such that strA's NUL terminator overlaps strW's NUL */ 328 size_t overlap_limit = (sizeof(strW)-sizeof(strA)) - (sizeof(strW[0])-sizeof(strA[0])); 329 330 SetLastError(0xdeadbeef); 331 memcpy(buf + 1, strW, sizeof(strW)); 332 ret = WideCharToMultiByte(CP_ACP, 0, (WCHAR *)(buf + 1), -1, buf, sizeof(buf), NULL, NULL); 333 ok(ret == sizeof(strA), "unexpected ret %d\n", ret); 334 ok(!memcmp(buf, strA, sizeof(strA)), "conversion failed: %s\n", buf); 335 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 336 337 SetLastError(0xdeadbeef); 338 memcpy(buf + overlap_limit, strA, sizeof(strA)); 339 ret = MultiByteToWideChar(CP_ACP, 0, buf + overlap_limit, -1, (WCHAR *)buf, sizeof(buf) / sizeof(WCHAR)); 340 ok(ret == ARRAY_SIZE(strW), "unexpected ret %d\n", ret); 341 ok(!memcmp(buf, strW, sizeof(strW)), "conversion failed: %s\n", wine_dbgstr_wn((WCHAR *)buf, ARRAY_SIZE(strW))); 342 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 343} 344 345static void test_string_conversion(LPBOOL bUsedDefaultChar) 346{ 347 char mbc; 348 char mbs[15]; 349 int ret; 350 WCHAR wc1 = 228; /* Western Windows-1252 character */ 351 WCHAR wc2 = 1088; /* Russian Windows-1251 character not displayable for Windows-1252 */ 352 static const WCHAR wcs[] = {'T', 'h', 1088, 'i', 0}; /* String with ASCII characters and a Russian character */ 353 static const WCHAR dbwcs[] = {28953, 25152, 0}; /* String with Chinese (codepage 950) characters */ 354 static const WCHAR dbwcs2[] = {0x7bb8, 0x3d, 0xc813, 0xac00, 0xb77d, 0}; 355 static const char default_char[] = {0xa3, 0xbf, 0}; 356 357 SetLastError(0xdeadbeef); 358 ret = WideCharToMultiByte(1252, 0, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar); 359 ok(ret == 1, "ret is %d\n", ret); 360 ok(mbc == '\xe4', "mbc is %d\n", mbc); 361 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 362 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 363 364 SetLastError(0xdeadbeef); 365 ret = WideCharToMultiByte(1252, 0, &wc2, 1, &mbc, 1, NULL, bUsedDefaultChar); 366 ok(ret == 1, "ret is %d\n", ret); 367 ok(mbc == 63, "mbc is %d\n", mbc); 368 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 369 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 370 371 if (IsValidCodePage(1251)) 372 { 373 SetLastError(0xdeadbeef); 374 ret = WideCharToMultiByte(1251, 0, &wc2, 1, &mbc, 1, NULL, bUsedDefaultChar); 375 ok(ret == 1, "ret is %d\n", ret); 376 ok(mbc == '\xf0', "mbc is %d\n", mbc); 377 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 378 ok(GetLastError() == 0xdeadbeef || 379 broken(GetLastError() == 0), /* win95 */ 380 "GetLastError() is %lu\n", GetLastError()); 381 382 SetLastError(0xdeadbeef); 383 ret = WideCharToMultiByte(1251, 0, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar); 384 ok(ret == 1, "ret is %d\n", ret); 385 ok(mbc == 97, "mbc is %d\n", mbc); 386 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 387 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 388 } 389 else 390 skip("Codepage 1251 not available\n"); 391 392 /* This call triggers the last Win32 error */ 393 SetLastError(0xdeadbeef); 394 ret = WideCharToMultiByte(1252, 0, wcs, -1, &mbc, 1, NULL, bUsedDefaultChar); 395 ok(ret == 0, "ret is %d\n", ret); 396 ok(mbc == 84, "mbc is %d\n", mbc); 397 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 398 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetLastError() is %lu\n", GetLastError()); 399 400 SetLastError(0xdeadbeef); 401 ret = WideCharToMultiByte(1252, 0, wcs, -1, mbs, sizeof(mbs), NULL, bUsedDefaultChar); 402 ok(ret == 5, "ret is %d\n", ret); 403 ok(!strcmp(mbs, "Th?i"), "mbs is %s\n", mbs); 404 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 405 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 406 mbs[0] = 0; 407 408 /* WideCharToMultiByte mustn't add any null character automatically. 409 So in this case, we should get the same string again, even if we only copied the first three bytes. */ 410 SetLastError(0xdeadbeef); 411 ret = WideCharToMultiByte(1252, 0, wcs, 3, mbs, sizeof(mbs), NULL, bUsedDefaultChar); 412 ok(ret == 3, "ret is %d\n", ret); 413 ok(!strcmp(mbs, "Th?i"), "mbs is %s\n", mbs); 414 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 415 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 416 ZeroMemory(mbs, 5); 417 418 /* Now this shouldn't be the case like above as we zeroed the complete string buffer. */ 419 SetLastError(0xdeadbeef); 420 ret = WideCharToMultiByte(1252, 0, wcs, 3, mbs, sizeof(mbs), NULL, bUsedDefaultChar); 421 ok(ret == 3, "ret is %d\n", ret); 422 ok(!strcmp(mbs, "Th?"), "mbs is %s\n", mbs); 423 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 424 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 425 426 /* Double-byte tests */ 427 ret = WideCharToMultiByte(1252, 0, dbwcs, 3, mbs, sizeof(mbs), NULL, bUsedDefaultChar); 428 ok(ret == 3, "ret is %d\n", ret); 429 ok(!strcmp(mbs, "??"), "mbs is %s\n", mbs); 430 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 431 432 ret = WideCharToMultiByte(936, WC_COMPOSITECHECK, dbwcs2, -1, mbs, sizeof(mbs), (const char *)default_char, bUsedDefaultChar); 433 ok(ret == 10, "ret is %d\n", ret); 434 ok(!strcmp(mbs, "\xf3\xe7\x3d\xa3\xbf\xa3\xbf\xa3\xbf"), "mbs is %s\n", mbs); 435 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 436 437 /* Show that characters are not truncated */ 438 ZeroMemory(mbs, 5); 439 ret = WideCharToMultiByte(936, 0, dbwcs2, -1, mbs, 1, (const char *)default_char, bUsedDefaultChar); 440 ok(!ret, "ret is %d\n", ret); 441 ok(mbs[0] == '\0', "mbs is %s\n", mbs); 442 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 443 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetLastError() is %lu\n", GetLastError()); 444 445 /* And destination is not null-terminated even when too short */ 446 FillMemory(mbs, 5, 'x'); 447 mbs[5] = '\0'; 448 ret = WideCharToMultiByte(936, 0, dbwcs2, -1, mbs, 2, (const char *)default_char, bUsedDefaultChar); 449 ok(!ret, "ret is %d\n", ret); 450 ok(!strcmp(mbs, "\xf3\xe7""xxx"), "mbs is %s\n", mbs); 451 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 452 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetLastError() is %lu\n", GetLastError()); 453 454 /* Length-only tests */ 455 SetLastError(0xdeadbeef); 456 ret = WideCharToMultiByte(1252, 0, &wc2, 1, NULL, 0, NULL, bUsedDefaultChar); 457 ok(ret == 1, "ret is %d\n", ret); 458 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 459 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 460 461 SetLastError(0xdeadbeef); 462 ret = WideCharToMultiByte(1252, 0, wcs, -1, NULL, 0, NULL, bUsedDefaultChar); 463 ok(ret == 5, "ret is %d\n", ret); 464 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 465 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 466 467 if (!IsValidCodePage(950)) 468 { 469 skip("Codepage 950 not available\n"); 470 return; 471 } 472 473 /* Double-byte tests */ 474 SetLastError(0xdeadbeef); 475 ret = WideCharToMultiByte(950, 0, dbwcs, -1, mbs, sizeof(mbs), NULL, bUsedDefaultChar); 476 ok(ret == 5, "ret is %d\n", ret); 477 ok(!strcmp(mbs, "\xb5H\xa9\xd2"), "mbs is %s\n", mbs); 478 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 479 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 480 481 SetLastError(0xdeadbeef); 482 ret = WideCharToMultiByte(950, 0, dbwcs, 1, &mbc, 1, NULL, bUsedDefaultChar); 483 ok(ret == 0, "ret is %d\n", ret); 484 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 485 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetLastError() is %lu\n", GetLastError()); 486 ZeroMemory(mbs, 5); 487 488 SetLastError(0xdeadbeef); 489 ret = WideCharToMultiByte(950, 0, dbwcs, 1, mbs, sizeof(mbs), NULL, bUsedDefaultChar); 490 ok(ret == 2, "ret is %d\n", ret); 491 ok(!strcmp(mbs, "\xb5H"), "mbs is %s\n", mbs); 492 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 493 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 494 495 /* Length-only tests */ 496 SetLastError(0xdeadbeef); 497 ret = WideCharToMultiByte(950, 0, dbwcs, 1, NULL, 0, NULL, bUsedDefaultChar); 498 ok(ret == 2, "ret is %d\n", ret); 499 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 500 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 501 502 SetLastError(0xdeadbeef); 503 ret = WideCharToMultiByte(950, 0, dbwcs, -1, NULL, 0, NULL, bUsedDefaultChar); 504 ok(ret == 5, "ret is %d\n", ret); 505 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar); 506 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %lu\n", GetLastError()); 507} 508 509static void test_utf7_encoding(void) 510{ 511 WCHAR input[16]; 512 char output[16], expected[16]; 513 int i, len, expected_len; 514 515 static const BOOL directly_encodable_table[] = 516 { 517 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */ 518 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */ 519 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */ 520 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */ 521 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */ 522 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */ 523 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */ 524 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 /* 0x70 - 0x7F */ 525 }; 526 static const char base64_encoding_table[] = 527 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 528 529 const struct 530 { 531 /* inputs */ 532 WCHAR src[16]; 533 int srclen; 534 char *dst; 535 int dstlen; 536 /* expected outputs */ 537 char expected_dst[16]; 538 int chars_written; 539 int len; 540 } 541 tests[] = 542 { 543 /* tests string conversion with srclen=-1 */ 544 { 545 {0x4F60,0x597D,0x5417,0}, -1, output, sizeof(output) - 1, 546 "+T2BZfVQX-", 11, 11 547 }, 548 /* tests string conversion with srclen=-2 */ 549 { 550 {0x4F60,0x597D,0x5417,0}, -2, output, sizeof(output) - 1, 551 "+T2BZfVQX-", 11, 11 552 }, 553 /* tests string conversion with dstlen=strlen(expected_dst) */ 554 { 555 {0x4F60,0x597D,0x5417,0}, -1, output, 10, 556 "+T2BZfVQX-", 10, 0 557 }, 558 /* tests string conversion with dstlen=strlen(expected_dst)+1 */ 559 { 560 {0x4F60,0x597D,0x5417,0}, -1, output, 11, 561 "+T2BZfVQX-", 11, 11 562 }, 563 /* tests string conversion with dstlen=strlen(expected_dst)+2 */ 564 { 565 {0x4F60,0x597D,0x5417,0}, -1, output, 12, 566 "+T2BZfVQX-", 11, 11 567 }, 568 /* tests dry run with dst=NULL and dstlen=0 */ 569 { 570 {0x4F60,0x597D,0x5417,0}, -1, NULL, 0, 571#if defined(__REACTOS__) && defined(_MSC_VER) 572 {0}, 0, 11 573#else 574 {}, 0, 11 575#endif 576 }, 577 /* tests dry run with dst!=NULL and dstlen=0 */ 578 { 579 {0x4F60,0x597D,0x5417,0}, -1, output, 0, 580#if defined(__REACTOS__) && defined(_MSC_VER) 581 {0}, 0, 11 582#else 583 {}, 0, 11 584#endif 585 }, 586 /* tests srclen < strlenW(src) with directly encodable chars */ 587 { 588 {'h','e','l','l','o',0}, 2, output, sizeof(output) - 1, 589 "he", 2, 2 590 }, 591 /* tests srclen < strlenW(src) with non-directly encodable chars */ 592 { 593 {0x4F60,0x597D,0x5417,0}, 2, output, sizeof(output) - 1, 594 "+T2BZfQ-", 8, 8 595 }, 596 /* tests a single null char */ 597 { 598 {0}, -1, output, sizeof(output) - 1, 599 "", 1, 1 600 }, 601 /* tests a buffer that runs out while not encoding a UTF-7 sequence */ 602 { 603 {'h','e','l','l','o',0}, -1, output, 2, 604 "he", 2, 0 605 }, 606 /* tests a buffer that runs out after writing 1 base64 character */ 607 { 608 {0x4F60,0x0001,0}, -1, output, 2, 609 "+T", 2, 0 610 }, 611 /* tests a buffer that runs out after writing 2 base64 characters */ 612 { 613 {0x4F60,0x0001,0}, -1, output, 3, 614 "+T2", 3, 0 615 }, 616 /* tests a buffer that runs out after writing 3 base64 characters */ 617 { 618 {0x4F60,0x0001,0}, -1, output, 4, 619 "+T2A", 4, 0 620 }, 621 /* tests a buffer that runs out just after writing the + sign */ 622 { 623 {0x4F60,0}, -1, output, 1, 624 "+", 1, 0 625 }, 626 /* tests a buffer that runs out just before writing the - sign 627 * the number of bits to encode here is evenly divisible by 6 */ 628 { 629 {0x4F60,0x597D,0x5417,0}, -1, output, 9, 630 "+T2BZfVQX", 9, 0 631 }, 632 /* tests a buffer that runs out just before writing the - sign 633 * the number of bits to encode here is NOT evenly divisible by 6 */ 634 { 635 {0x4F60,0}, -1, output, 4, 636 "+T2", 3, 0 637 }, 638 /* tests a buffer that runs out in the middle of escaping a + sign */ 639 { 640 {'+',0}, -1, output, 1, 641 "+", 1, 0 642 } 643 }; 644 645 /* test which characters are encoded if surrounded by non-encoded characters */ 646 for (i = 0; i <= 0xFFFF; i++) 647 { 648 input[0] = ' '; 649 input[1] = i; 650 input[2] = ' '; 651 input[3] = 0; 652 653 memset(output, '#', sizeof(output) - 1); 654 output[sizeof(output) - 1] = 0; 655 656 len = WideCharToMultiByte(CP_UTF7, 0, input, 4, output, sizeof(output) - 1, NULL, NULL); 657 658 if (i == '+') 659 { 660 /* '+' is a special case and is encoded as "+-" */ 661 expected_len = 5; 662 strcpy(expected, " +- "); 663 } 664 else if (i <= 0x7F && directly_encodable_table[i]) 665 { 666 /* encodes directly */ 667 expected_len = 4; 668 sprintf(expected, " %c ", i); 669 } 670 else 671 { 672 /* base64-encodes */ 673 expected_len = 8; 674 sprintf(expected, " +%c%c%c- ", 675 base64_encoding_table[(i & 0xFC00) >> 10], 676 base64_encoding_table[(i & 0x03F0) >> 4], 677 base64_encoding_table[(i & 0x000F) << 2]); 678 } 679 680 ok(len == expected_len, "i=0x%04x: expected len=%i, got len=%i\n", i, expected_len, len); 681 ok(memcmp(output, expected, expected_len) == 0, 682 "i=0x%04x: expected output='%s', got output='%s'\n", i, expected, output); 683 ok(output[expected_len] == '#', "i=0x%04x: expected output[%i]='#', got output[%i]=%i\n", 684 i, expected_len, expected_len, output[expected_len]); 685 } 686 687 /* test which one-byte characters are absorbed into surrounding base64 blocks 688 * (Windows always ends the base64 block when it encounters a directly encodable character) */ 689 for (i = 0; i <= 0xFFFF; i++) 690 { 691 input[0] = 0x2672; 692 input[1] = i; 693 input[2] = 0x2672; 694 input[3] = 0; 695 696 memset(output, '#', sizeof(output) - 1); 697 output[sizeof(output) - 1] = 0; 698 699 len = WideCharToMultiByte(CP_UTF7, 0, input, 4, output, sizeof(output) - 1, NULL, NULL); 700 701 if (i == '+') 702 { 703 /* '+' is a special case and is encoded as "+-" */ 704 expected_len = 13; 705 strcpy(expected, "+JnI-+-+JnI-"); 706 } 707 else if (i <= 0x7F && directly_encodable_table[i]) 708 { 709 /* encodes directly */ 710 expected_len = 12; 711 sprintf(expected, "+JnI-%c+JnI-", i); 712 } 713 else 714 { 715 /* base64-encodes */ 716 expected_len = 11; 717 sprintf(expected, "+Jn%c%c%c%cZy-", 718 base64_encoding_table[8 | ((i & 0xC000) >> 14)], 719 base64_encoding_table[(i & 0x3F00) >> 8], 720 base64_encoding_table[(i & 0x00FC) >> 2], 721 base64_encoding_table[((i & 0x0003) << 4) | 2]); 722 } 723 724 ok(len == expected_len, "i=0x%04x: expected len=%i, got len=%i\n", i, expected_len, len); 725 ok(memcmp(output, expected, expected_len) == 0, 726 "i=0x%04x: expected output='%s', got output='%s'\n", i, expected, output); 727 ok(output[expected_len] == '#', "i=0x%04x: expected output[%i]='#', got output[%i]=%i\n", 728 i, expected_len, expected_len, output[expected_len]); 729 } 730 731 for (i = 0; i < ARRAY_SIZE(tests); i++) 732 { 733 memset(output, '#', sizeof(output) - 1); 734 output[sizeof(output) - 1] = 0; 735 SetLastError(0xdeadbeef); 736 737 len = WideCharToMultiByte(CP_UTF7, 0, tests[i].src, tests[i].srclen, 738 tests[i].dst, tests[i].dstlen, NULL, NULL); 739 740 if (!tests[i].len) 741 { 742 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, 743 "tests[%i]: expected error=0x%x, got error=0x%lx\n", 744 i, ERROR_INSUFFICIENT_BUFFER, GetLastError()); 745 } 746 ok(len == tests[i].len, "tests[%i]: expected len=%i, got len=%i\n", i, tests[i].len, len); 747 748 if (tests[i].dst) 749 { 750 ok(memcmp(tests[i].dst, tests[i].expected_dst, tests[i].chars_written) == 0, 751 "tests[%i]: expected dst='%s', got dst='%s'\n", 752 i, tests[i].expected_dst, tests[i].dst); 753 ok(tests[i].dst[tests[i].chars_written] == '#', 754 "tests[%i]: expected dst[%i]='#', got dst[%i]=%i\n", 755 i, tests[i].chars_written, tests[i].chars_written, tests[i].dst[tests[i].chars_written]); 756 } 757 } 758} 759 760static void test_utf7_decoding(void) 761{ 762 char input[32]; 763 WCHAR output[32], expected[32]; 764 int i, len, expected_len; 765 766 static const signed char base64_decoding_table[] = 767 { 768 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */ 769 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */ 770 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */ 771 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */ 772 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */ 773 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */ 774 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */ 775 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */ 776 }; 777 778 struct 779 { 780 /* inputs */ 781 char src[32]; 782 int srclen; 783 WCHAR *dst; 784 int dstlen; 785 /* expected outputs */ 786 WCHAR expected_dst[32]; 787 int chars_written; 788 int len; 789 } 790 tests[] = 791 { 792 /* tests string conversion with srclen=-1 */ 793 { 794 "+T2BZfQ-", -1, output, ARRAY_SIZE(output) - 1, 795 {0x4F60,0x597D,0}, 3, 3 796 }, 797 /* tests string conversion with srclen=-2 */ 798 { 799 "+T2BZfQ-", -2, output, ARRAY_SIZE(output) - 1, 800 {0x4F60,0x597D,0}, 3, 3 801 }, 802 /* tests string conversion with dstlen=strlen(expected_dst) */ 803 { 804 "+T2BZfQ-", -1, output, 2, 805 {0x4F60,0x597D}, 2, 0 806 }, 807 /* tests string conversion with dstlen=strlen(expected_dst)+1 */ 808 { 809 "+T2BZfQ-", -1, output, 3, 810 {0x4F60,0x597D,0}, 3, 3 811 }, 812 /* tests string conversion with dstlen=strlen(expected_dst)+2 */ 813 { 814 "+T2BZfQ-", -1, output, 4, 815 {0x4F60,0x597D,0}, 3, 3 816 }, 817 /* tests dry run with dst=NULL and dstlen=0 */ 818 { 819 "+T2BZfQ-", -1, NULL, 0, 820#if defined(__REACTOS__) && defined(_MSC_VER) 821 {0}, 0, 3 822#else 823 {}, 0, 3 824#endif 825 }, 826 /* tests dry run with dst!=NULL and dstlen=0 */ 827 { 828 "+T2BZfQ-", -1, output, 0, 829#if defined(__REACTOS__) && defined(_MSC_VER) 830 {0}, 0, 3 831#else 832 {}, 0, 3 833#endif 834 }, 835 /* tests ill-formed UTF-7: 6 bits, not enough for a byte pair */ 836 { 837 "+T-+T-+T-hello", -1, output, ARRAY_SIZE(output) - 1, 838 {'h','e','l','l','o',0}, 6, 6 839 }, 840 /* tests ill-formed UTF-7: 12 bits, not enough for a byte pair */ 841 { 842 "+T2-+T2-+T2-hello", -1, output, ARRAY_SIZE(output) - 1, 843 {'h','e','l','l','o',0}, 6, 6 844 }, 845 /* tests ill-formed UTF-7: 18 bits, not a multiple of 16 and the last bit is a 1 */ 846 { 847 "+T2B-+T2B-+T2B-hello", -1, output, ARRAY_SIZE(output) - 1, 848 {0x4F60,0x4F60,0x4F60,'h','e','l','l','o',0}, 9, 9 849 }, 850 /* tests ill-formed UTF-7: 24 bits, a multiple of 8 but not a multiple of 16 */ 851 { 852 "+T2BZ-+T2BZ-+T2BZ-hello", -1, output, ARRAY_SIZE(output) - 1, 853 {0x4F60,0x4F60,0x4F60,'h','e','l','l','o',0}, 9, 9 854 }, 855 /* tests UTF-7 followed by characters that should be encoded but aren't */ 856 { 857 "+T2BZ-\x82\xFE", -1, output, ARRAY_SIZE(output) - 1, 858 {0x4F60,0x0082,0x00FE,0}, 4, 4 859 }, 860 /* tests srclen > strlen(src) */ 861 { 862 "a\0b", 4, output, ARRAY_SIZE(output) - 1, 863 {'a',0,'b',0}, 4, 4 864 }, 865 /* tests srclen < strlen(src) outside of a UTF-7 sequence */ 866 { 867 "hello", 2, output, ARRAY_SIZE(output) - 1, 868 {'h','e'}, 2, 2 869 }, 870 /* tests srclen < strlen(src) inside of a UTF-7 sequence */ 871 { 872 "+T2BZfQ-", 4, output, ARRAY_SIZE(output) - 1, 873 {0x4F60}, 1, 1 874 }, 875 /* tests srclen < strlen(src) right at the beginning of a UTF-7 sequence */ 876 { 877 "hi+T2A-", 3, output, ARRAY_SIZE(output) - 1, 878 {'h','i'}, 2, 2 879 }, 880 /* tests srclen < strlen(src) right at the end of a UTF-7 sequence */ 881 { 882 "+T2A-hi", 5, output, ARRAY_SIZE(output) - 1, 883 {0x4F60}, 1, 1 884 }, 885 /* tests srclen < strlen(src) at the beginning of an escaped + sign */ 886 { 887 "hi+-", 3, output, ARRAY_SIZE(output) - 1, 888 {'h','i'}, 2, 2 889 }, 890 /* tests srclen < strlen(src) at the end of an escaped + sign */ 891 { 892 "+-hi", 2, output, ARRAY_SIZE(output) - 1, 893 {'+'}, 1, 1 894 }, 895 /* tests len=0 but no error */ 896 { 897 "+", 1, output, ARRAY_SIZE(output) - 1, 898#if defined(__REACTOS__) && defined(_MSC_VER) 899 {0}, 0, 0 900#else 901 {}, 0, 0 902#endif 903 }, 904 /* tests a single null char */ 905 { 906 "", -1, output, ARRAY_SIZE(output) - 1, 907 {0}, 1, 1 908 }, 909 /* tests a buffer that runs out while not decoding a UTF-7 sequence */ 910 { 911 "hello", -1, output, 2, 912 {'h','e'}, 2, 0 913 }, 914 /* tests a buffer that runs out in the middle of decoding a UTF-7 sequence */ 915 { 916 "+T2BZfQ-", -1, output, 1, 917 {0x4F60}, 1, 0 918 } 919 }; 920 921 /* test which one-byte characters remove stray + signs */ 922 for (i = 0; i < 256; i++) 923 { 924 sprintf(input, "+%c+AAA", i); 925 926 memset(output, 0x23, sizeof(output) - sizeof(WCHAR)); 927 output[ARRAY_SIZE(output) - 1] = 0; 928 929 len = MultiByteToWideChar(CP_UTF7, 0, input, 7, output, ARRAY_SIZE(output) - 1); 930 931 if (i == '-') 932 { 933 /* removes the - sign */ 934 expected_len = 3; 935 expected[0] = 0x002B; 936 expected[1] = 0; 937 expected[2] = 0; 938 } 939 else if (i <= 0x7F && base64_decoding_table[i] != -1) 940 { 941 /* absorbs the character into the base64 sequence */ 942 expected_len = 2; 943 expected[0] = (base64_decoding_table[i] << 10) | 0x03E0; 944 expected[1] = 0; 945 } 946 else 947 { 948 /* removes the + sign */ 949 expected_len = 3; 950 expected[0] = i; 951 expected[1] = 0; 952 expected[2] = 0; 953 } 954 expected[expected_len] = 0x2323; 955 956 ok(len == expected_len, "i=0x%02x: expected len=%i, got len=%i\n", i, expected_len, len); 957 ok(memcmp(output, expected, (expected_len + 1) * sizeof(WCHAR)) == 0, 958 "i=0x%02x: expected output=%s, got output=%s\n", 959 i, wine_dbgstr_wn(expected, expected_len + 1), wine_dbgstr_wn(output, expected_len + 1)); 960 } 961 962 /* test which one-byte characters terminate a sequence 963 * also test whether the unfinished byte pair is discarded or not */ 964 for (i = 0; i < 256; i++) 965 { 966 sprintf(input, "+B%c+AAA", i); 967 968 memset(output, 0x23, sizeof(output) - sizeof(WCHAR)); 969 output[ARRAY_SIZE(output) - 1] = 0; 970 971 len = MultiByteToWideChar(CP_UTF7, 0, input, 8, output, ARRAY_SIZE(output) - 1); 972 973 if (i == '-') 974 { 975 /* explicitly terminates */ 976 expected_len = 2; 977 expected[0] = 0; 978 expected[1] = 0; 979 } 980 else if (i <= 0x7F) 981 { 982 if (base64_decoding_table[i] != -1) 983 { 984 /* absorbs the character into the base64 sequence */ 985 expected_len = 3; 986 expected[0] = 0x0400 | (base64_decoding_table[i] << 4) | 0x000F; 987 expected[1] = 0x8000; 988 expected[2] = 0; 989 } 990 else 991 { 992 /* implicitly terminates and discards the unfinished byte pair */ 993 expected_len = 3; 994 expected[0] = i; 995 expected[1] = 0; 996 expected[2] = 0; 997 } 998 } 999 else 1000 { 1001 /* implicitly terminates but does not the discard unfinished byte pair */ 1002 expected_len = 3; 1003 expected[0] = i; 1004 expected[1] = 0x0400; 1005 expected[2] = 0; 1006 } 1007 expected[expected_len] = 0x2323; 1008 1009 ok(len == expected_len, "i=0x%02x: expected len=%i, got len=%i\n", i, expected_len, len); 1010 ok(memcmp(output, expected, (expected_len + 1) * sizeof(WCHAR)) == 0, 1011 "i=0x%02x: expected output=%s, got output=%s\n", 1012 i, wine_dbgstr_wn(expected, expected_len + 1), wine_dbgstr_wn(output, expected_len + 1)); 1013 } 1014 1015 for (i = 0; i < ARRAY_SIZE(tests); i++) 1016 { 1017 memset(output, 0x23, sizeof(output) - sizeof(WCHAR)); 1018 output[ARRAY_SIZE(output) - 1] = 0; 1019 SetLastError(0xdeadbeef); 1020 1021 len = MultiByteToWideChar(CP_UTF7, 0, tests[i].src, tests[i].srclen, 1022 tests[i].dst, tests[i].dstlen); 1023 1024 tests[i].expected_dst[tests[i].chars_written] = 0x2323; 1025 1026 if (!tests[i].len && tests[i].chars_written) 1027 { 1028 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, 1029 "tests[%i]: expected error=0x%x, got error=0x%lx\n", 1030 i, ERROR_INSUFFICIENT_BUFFER, GetLastError()); 1031 } 1032 ok(len == tests[i].len, "tests[%i]: expected len=%i, got len=%i\n", i, tests[i].len, len); 1033 1034 if (tests[i].dst) 1035 { 1036 ok(memcmp(tests[i].dst, tests[i].expected_dst, (tests[i].chars_written + 1) * sizeof(WCHAR)) == 0, 1037 "tests[%i]: expected dst=%s, got dst=%s\n", 1038 i, wine_dbgstr_wn(tests[i].expected_dst, tests[i].chars_written + 1), 1039 wine_dbgstr_wn(tests[i].dst, tests[i].chars_written + 1)); 1040 } 1041 } 1042} 1043 1044static void test_undefined_byte_char(void) 1045{ 1046 static const struct tag_testset { 1047 UINT codepage; 1048 LPCSTR str; 1049 BOOL is_error; 1050 } testset[] = { 1051 { 37, "\x6f", FALSE }, 1052 { 874, "\xdd", TRUE }, 1053 { 932, "\xfe", TRUE }, 1054 { 932, "\x80", FALSE }, 1055 { 932, "\x81\x45", FALSE }, 1056 { 936, "\xff", TRUE }, 1057 { 949, "\xff", TRUE }, 1058 { 950, "\xff", TRUE }, 1059 { 1252, "?", FALSE }, 1060 { 1252, "\x90", FALSE }, 1061 { 1253, "\xaa", TRUE }, 1062 { 1255, "\xff", TRUE }, 1063 { 1257, "\xa5", TRUE }, 1064 }; 1065 INT i, ret; 1066 DWORD err; 1067 1068 for (i = 0; i < ARRAY_SIZE(testset); i++) { 1069 if (! IsValidCodePage(testset[i].codepage)) 1070 { 1071 skip("Codepage %d not available\n", testset[i].codepage); 1072 continue; 1073 } 1074 1075 SetLastError(0xdeadbeef); 1076 ret = MultiByteToWideChar(testset[i].codepage, MB_ERR_INVALID_CHARS, 1077 testset[i].str, -1, NULL, 0); 1078 err = GetLastError(); 1079 if (testset[i].is_error) { 1080 ok(err == ERROR_NO_UNICODE_TRANSLATION, "Test %u: err is %lu\n", i, err); 1081 ok(ret == 0, "Test %u: ret is %d\n", i, ret); 1082 } 1083 else { 1084 ok(err == 0xdeadbeef, "Test %u: err is %lu\n", i, err); 1085 ok(ret == 2, "Test %u: ret is %d\n", i, ret); 1086 } 1087 1088 SetLastError(0xdeadbeef); 1089 ret = MultiByteToWideChar(testset[i].codepage, 0, 1090 testset[i].str, -1, NULL, 0); 1091 err = GetLastError(); 1092 ok(err == 0xdeadbeef, "Test %u: err is %lu\n", i, err); 1093 ok(ret == 2, "Test %u: ret is %d\n", i, ret); 1094 } 1095} 1096 1097static void test_threadcp(void) 1098{ 1099 static const LCID ENGLISH = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); 1100 static const LCID HINDI = MAKELCID(MAKELANGID(LANG_HINDI, SUBLANG_HINDI_INDIA), SORT_DEFAULT); 1101 static const LCID GEORGIAN = MAKELCID(MAKELANGID(LANG_GEORGIAN, SUBLANG_GEORGIAN_GEORGIA), SORT_DEFAULT); 1102 static const LCID RUSSIAN = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_RUSSIAN_RUSSIA), SORT_DEFAULT); 1103 static const LCID JAPANESE = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), SORT_DEFAULT); 1104 static const LCID CHINESE = MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT); 1105 1106 BOOL islead, islead_default; 1107 CPINFOEXA cpi; 1108 UINT cp, acp; 1109 int i, num; 1110 LCID last; 1111 BOOL ret; 1112 1113 struct test { 1114 LCID lcid; 1115 UINT threadcp; 1116 } lcids[] = { 1117 { HINDI, 0 }, 1118 { GEORGIAN, 0 }, 1119 { ENGLISH, 1252 }, 1120 { RUSSIAN, 1251 }, 1121 { JAPANESE, 932 }, 1122 { CHINESE, 936 } 1123 }; 1124 1125 struct test_islead_nocp { 1126 LCID lcid; 1127 BYTE testchar; 1128 } isleads_nocp[] = { 1129 { HINDI, 0x00 }, 1130 { HINDI, 0x81 }, 1131 { HINDI, 0xa0 }, 1132 { HINDI, 0xe0 }, 1133 1134 { GEORGIAN, 0x00 }, 1135 { GEORGIAN, 0x81 }, 1136 { GEORGIAN, 0xa0 }, 1137 { GEORGIAN, 0xe0 }, 1138 }; 1139 1140 struct test_islead { 1141 LCID lcid; 1142 BYTE testchar; 1143 BOOL islead; 1144 } isleads[] = { 1145 { ENGLISH, 0x00, FALSE }, 1146 { ENGLISH, 0x81, FALSE }, 1147 { ENGLISH, 0xa0, FALSE }, 1148 { ENGLISH, 0xe0, FALSE }, 1149 1150 { RUSSIAN, 0x00, FALSE }, 1151 { RUSSIAN, 0x81, FALSE }, 1152 { RUSSIAN, 0xa0, FALSE }, 1153 { RUSSIAN, 0xe0, FALSE }, 1154 1155 { JAPANESE, 0x00, FALSE }, 1156 { JAPANESE, 0x81, TRUE }, 1157 { JAPANESE, 0xa0, FALSE }, 1158 { JAPANESE, 0xe0, TRUE }, 1159 1160 { CHINESE, 0x00, FALSE }, 1161 { CHINESE, 0x81, TRUE }, 1162 { CHINESE, 0xa0, TRUE }, 1163 { CHINESE, 0xe0, TRUE }, 1164 }; 1165 1166 last = GetThreadLocale(); 1167 acp = GetACP(); 1168 1169 for (i = 0; i < ARRAY_SIZE(lcids); i++) 1170 { 1171 SetThreadLocale(lcids[i].lcid); 1172 1173 cp = 0xdeadbeef; 1174 GetLocaleInfoA(lcids[i].lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (LPSTR)&cp, sizeof(cp)); 1175 ok(cp == lcids[i].threadcp, "wrong codepage %u for lcid %04lx, should be %u\n", cp, lcids[i].lcid, lcids[i].threadcp); 1176 1177 /* GetCPInfoEx/GetCPInfo - CP_ACP */ 1178 SetLastError(0xdeadbeef); 1179 memset(&cpi, 0, sizeof(cpi)); 1180 ret = GetCPInfoExA(CP_ACP, 0, &cpi); 1181 ok(ret, "GetCPInfoExA failed for lcid %04lx, error %ld\n", lcids[i].lcid, GetLastError()); 1182 ok(cpi.CodePage == acp, "wrong codepage %u for lcid %04lx, should be %u\n", cpi.CodePage, lcids[i].lcid, acp); 1183 1184 /* WideCharToMultiByte - CP_ACP */ 1185 num = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, NULL, 0, NULL, NULL); 1186 ok(num == 7, "ret is %d (%04lx)\n", num, lcids[i].lcid); 1187 1188 /* MultiByteToWideChar - CP_ACP */ 1189 num = MultiByteToWideChar(CP_ACP, 0, "foobar", -1, NULL, 0); 1190 ok(num == 7, "ret is %d (%04lx)\n", num, lcids[i].lcid); 1191 1192 /* GetCPInfoEx/GetCPInfo - CP_THREAD_ACP */ 1193 SetLastError(0xdeadbeef); 1194 memset(&cpi, 0, sizeof(cpi)); 1195 ret = GetCPInfoExA(CP_THREAD_ACP, 0, &cpi); 1196 ok(ret, "GetCPInfoExA failed for lcid %04lx, error %ld\n", lcids[i].lcid, GetLastError()); 1197 if (lcids[i].threadcp) 1198 ok(cpi.CodePage == lcids[i].threadcp || cpi.CodePage == CP_UTF8 /* Win10 1809+ */, 1199 "wrong codepage %u for lcid %04lx, should be %u\n", 1200 cpi.CodePage, lcids[i].lcid, lcids[i].threadcp); 1201 else 1202 ok(cpi.CodePage == acp || cpi.CodePage == CP_UTF8 /* Win10 1809+ */, 1203 "wrong codepage %u for lcid %04lx, should be %u\n", cpi.CodePage, lcids[i].lcid, acp); 1204 1205 /* WideCharToMultiByte - CP_THREAD_ACP */ 1206 num = WideCharToMultiByte(CP_THREAD_ACP, 0, foobarW, -1, NULL, 0, NULL, NULL); 1207 ok(num == 7, "ret is %d (%04lx)\n", num, lcids[i].lcid); 1208 1209 /* MultiByteToWideChar - CP_THREAD_ACP */ 1210 num = MultiByteToWideChar(CP_THREAD_ACP, 0, "foobar", -1, NULL, 0); 1211 ok(num == 7, "ret is %d (%04lx)\n", num, lcids[i].lcid); 1212 } 1213 1214 /* IsDBCSLeadByteEx - locales without codepage */ 1215 for (i = 0; i < ARRAY_SIZE(isleads_nocp); i++) 1216 { 1217 SetThreadLocale(isleads_nocp[i].lcid); 1218 1219 GetCPInfoExA(CP_THREAD_ACP, 0, &cpi); 1220 islead_default = IsDBCSLeadByteEx(cpi.CodePage, isleads_nocp[i].testchar); 1221 islead = IsDBCSLeadByteEx(CP_THREAD_ACP, isleads_nocp[i].testchar); 1222 1223 ok(islead == islead_default, "wrong islead %i for test char %x in lcid %04lx. should be %i\n", 1224 islead, isleads_nocp[i].testchar, isleads_nocp[i].lcid, islead_default); 1225 } 1226 1227 /* IsDBCSLeadByteEx - locales with codepage */ 1228 for (i = 0; i < ARRAY_SIZE(isleads); i++) 1229 { 1230 SetThreadLocale(isleads[i].lcid); 1231 1232 GetCPInfoExA(CP_THREAD_ACP, 0, &cpi); 1233 islead = IsDBCSLeadByteEx(CP_THREAD_ACP, isleads[i].testchar); 1234 ok(islead == isleads[i].islead || (cpi.CodePage == CP_UTF8 && !islead), 1235 "wrong islead %i for test char %x in lcid %04lx. should be %i\n", 1236 islead, isleads[i].testchar, isleads[i].lcid, isleads[i].islead); 1237 } 1238 1239 SetThreadLocale(last); 1240} 1241 1242static void test_dbcs_to_widechar(void) 1243{ 1244 int i, count, count2; 1245 WCHAR wbuf[5]; 1246 unsigned char buf[] = {0xbf, 0xb4, 0xc7, '\0', 'x'}; 1247 static const DWORD flags[] = { 1248 MB_PRECOMPOSED, 1249 MB_COMPOSITE, 1250 1251 MB_PRECOMPOSED|MB_USEGLYPHCHARS, 1252 MB_COMPOSITE |MB_USEGLYPHCHARS, 1253 1254 MB_PRECOMPOSED|MB_ERR_INVALID_CHARS, 1255 MB_COMPOSITE |MB_ERR_INVALID_CHARS, 1256 1257 MB_PRECOMPOSED|MB_ERR_INVALID_CHARS|MB_USEGLYPHCHARS, 1258 MB_COMPOSITE |MB_ERR_INVALID_CHARS|MB_USEGLYPHCHARS, 1259 }; 1260 1261 for (i = 0; i < ARRAY_SIZE(flags); ++i) 1262 { 1263 memset(wbuf, 0xff, sizeof(wbuf)); 1264 count = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 2, NULL, 0); 1265 count2 = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 2, wbuf, count); 1266 1267 ok(count == 1, "%04lx: returned %d (expected 1)\n", flags[i], count); 1268 ok(count2 == 1, "%04lx: returned %d (expected 1)\n", flags[i], count2); 1269 ok(wbuf[0] == 0x770b, "%04lx: returned %04x (expected 770b)\n", flags[i], wbuf[0]); 1270 ok(wbuf[1] == 0xffff, "%04lx: returned %04x (expected ffff)\n", flags[i], wbuf[1]); 1271 } 1272 1273 for (i = 0; i < ARRAY_SIZE(flags); ++i) 1274 { 1275 memset(wbuf, 0xff, sizeof(wbuf)); 1276 count = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 3, NULL, 0); 1277 SetLastError( 0xdeadbeef ); 1278 count2 = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 3, wbuf, count); 1279 1280 if (flags[i] & MB_ERR_INVALID_CHARS) 1281 { 1282 ok(count == 0, "%04lx: returned %d (expected 0)\n", flags[i], count); 1283 ok(count2 == 0, "%04lx: returned %d (expected 0)\n", flags[i], count2); 1284 ok(GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%04lx: returned %ld (expected %d)\n", 1285 flags[i], GetLastError(), ERROR_NO_UNICODE_TRANSLATION); 1286 } 1287 else 1288 { 1289 ok(count == 2, "%04lx: returned %d (expected 2)\n", flags[i], count); 1290 ok(count2 == 2, "%04lx: returned %d (expected 2)\n", flags[i], count2); 1291 ok(wbuf[0] == 0x770b, "%04lx: returned %04x (expected 770b)\n", flags[i], wbuf[0]); 1292 ok(wbuf[1] == 0x003f || broken(wbuf[1] == 0), /*windows xp*/ 1293 "%04lx: wrong wide char: %04x\n", flags[i], wbuf[1]); 1294 ok(wbuf[2] == 0xffff, "%04lx: returned %04x (expected ffff)\n", flags[i], wbuf[2]); 1295 } 1296 } 1297 1298 /* src ends with null character */ 1299 for (i = 0; i < ARRAY_SIZE(flags); ++i) 1300 { 1301 memset(wbuf, 0xff, sizeof(wbuf)); 1302 count = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 4, NULL, 0); 1303 SetLastError( 0xdeadbeef ); 1304 count2 = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 4, wbuf, count); 1305 ok(count == count2, "%04lx: returned %d (expected %d)\n", flags[i], count2, count); 1306 1307 if (flags[i] & MB_ERR_INVALID_CHARS) 1308 { 1309 ok(count == 0, "%04lx: returned %d (expected 0)\n", flags[i], count); 1310 ok(GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%04lx: returned %ld (expected %d)\n", 1311 flags[i], GetLastError(), ERROR_NO_UNICODE_TRANSLATION); 1312 } 1313 else 1314 { 1315 WCHAR wbuf_ok[] = { 0x770b, 0x003f, '\0', 0xffff }; 1316 WCHAR wbuf_broken[] = { 0x770b, '\0', 0xffff, 0xffff }; 1317 ok(count == 3 || broken(count == 2 /*windows xp*/), 1318 "%04lx: returned %d (expected 3)\n", flags[i], count); 1319 ok(!memcmp(wbuf, wbuf_ok, sizeof(wbuf_ok)) 1320 || broken(!memcmp(wbuf, wbuf_broken, sizeof(wbuf_broken))), 1321 "%04lx: returned %04x %04x %04x %04x (expected %04x %04x %04x %04x)\n", 1322 flags[i], wbuf[0], wbuf[1], wbuf[2], wbuf[3], 1323 wbuf_ok[0], wbuf_ok[1], wbuf_ok[2], wbuf_ok[3]); 1324 } 1325 } 1326 1327 /* src has null character, but not ends with it */ 1328 for (i = 0; i < ARRAY_SIZE(flags); ++i) 1329 { 1330 memset(wbuf, 0xff, sizeof(wbuf)); 1331 count = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 5, NULL, 0); 1332 SetLastError( 0xdeadbeef ); 1333 count2 = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 5, wbuf, count); 1334 ok(count == count2, "%04lx: returned %d (expected %d)\n", flags[i], count2, count); 1335 1336 if (flags[i] & MB_ERR_INVALID_CHARS) 1337 { 1338 ok(count == 0, "%04lx: returned %d (expected 0)\n", flags[i], count); 1339 ok(GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%04lx: returned %ld (expected %d)\n", 1340 flags[i], GetLastError(), ERROR_NO_UNICODE_TRANSLATION); 1341 } 1342 else 1343 { 1344 WCHAR wbuf_ok[] = { 0x770b, 0x003f, '\0', 'x', 0xffff }; 1345 WCHAR wbuf_broken[] = { 0x770b, '\0', 'x', 0xffff, 0xffff }; 1346 ok(count == 4 || broken(count == 3), 1347 "%04lx: returned %d (expected 4)\n", flags[i], count); 1348 ok(!memcmp(wbuf, wbuf_ok, sizeof(wbuf_ok)) 1349 || broken(!memcmp(wbuf, wbuf_broken, sizeof(wbuf_broken))), 1350 "%04lx: returned %04x %04x %04x %04x %04x (expected %04x %04x %04x %04x %04x)\n", 1351 flags[i], wbuf[0], wbuf[1], wbuf[2], wbuf[3], wbuf[4], 1352 wbuf_ok[0], wbuf_ok[1], wbuf_ok[2], wbuf_ok[3], wbuf_ok[4]); 1353 } 1354 } 1355} 1356 1357START_TEST(codepage) 1358{ 1359 BOOL bUsedDefaultChar; 1360 1361 test_destination_buffer(); 1362 test_null_source(); 1363 test_negative_source_length(); 1364 test_negative_dest_length(); 1365 test_other_invalid_parameters(); 1366 test_overlapped_buffers(); 1367 1368 /* WideCharToMultiByte has two code paths, test both here */ 1369 test_string_conversion(NULL); 1370 test_string_conversion(&bUsedDefaultChar); 1371 1372 test_utf7_encoding(); 1373 test_utf7_decoding(); 1374 1375 test_undefined_byte_char(); 1376 test_threadcp(); 1377 1378 test_dbcs_to_widechar(); 1379}