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