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