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 
test_destination_buffer(void)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 
test_null_source(void)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 
test_negative_source_length(void)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
test_negative_dest_length(void)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 
test_other_invalid_parameters(void)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 
test_overlapped_buffers(void)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 
test_string_conversion(LPBOOL bUsedDefaultChar)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 
test_utf7_encoding(void)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 
test_utf7_decoding(void)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 
test_undefined_byte_char(void)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 
test_threadcp(void)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 
test_dbcs_to_widechar(void)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 
START_TEST(codepage)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