Recuce DLL hijack risk on Windows
[platform/upstream/glib.git] / glib / win_iconv.c
1 /*\r
2  * iconv library implemented with Win32 API.\r
3  *\r
4  * This file is placed in the public domain.\r
5  *\r
6  * Maintainer: Yukihiro Nakadaira <yukihiro.nakadaira@gmail.com>\r
7  *\r
8  * If $WINICONV_LIBICONV_DLL environment variable was defined, win_iconv\r
9  * loads the specified DLL dynamically and uses it.  If loading the DLL\r
10  * or iconv_open() failed, falls back to internal conversion.\r
11  * $WINICONV_LIBICONV_DLL is a comma separated list.  The first loadable\r
12  * DLL is used.  The specified DLL should have iconv_open(),\r
13  * iconv_close() and iconv() functions.  Or these functions can be\r
14  * libiconv_open(), libiconv_close() and libiconv().\r
15  *\r
16  * Win32 API does not support strict encoding conversion for some\r
17  * codepage.  And MLang function drop or replace invalid bytes and does\r
18  * not return useful error status as iconv.  This implementation cannot\r
19  * be used for encoding validation purpose.\r
20  */\r
21 \r
22 /* for WC_NO_BEST_FIT_CHARS */\r
23 #ifndef WINVER\r
24 # define WINVER 0x0500\r
25 #endif\r
26 \r
27 #define STRICT\r
28 #include <windows.h>\r
29 #include <errno.h>\r
30 #include <string.h>\r
31 #include <stdlib.h>\r
32 \r
33 #if 0\r
34 # define MAKE_EXE\r
35 # define MAKE_DLL\r
36 # define USE_LIBICONV_DLL\r
37 #endif\r
38 \r
39 #if !defined(DEFAULT_LIBICONV_DLL)\r
40 # define DEFAULT_LIBICONV_DLL ""\r
41 #endif\r
42 \r
43 #define MB_CHAR_MAX 16\r
44 \r
45 #define UNICODE_MODE_BOM_DONE   1\r
46 #define UNICODE_MODE_SWAPPED    2\r
47 \r
48 #define FLAG_USE_BOM_ENDIAN     1\r
49 #define FLAG_TRANSLIT           2 /* //TRANSLIT */\r
50 #define FLAG_IGNORE             4 /* //IGNORE (not implemented) */\r
51 \r
52 #define return_error(code)  \\r
53     do {                    \\r
54         errno = code;       \\r
55         return -1;          \\r
56     } while (0)\r
57 \r
58 #define xstrlcpy(dst, src, size)    \\r
59     do {                            \\r
60         strncpy(dst, src, size);    \\r
61         dst[size - 1] = 0;          \\r
62     } while (0)\r
63 \r
64 #define xstrlcpyn(dst, src, srclen, size) \\r
65     xstrlcpy(dst, src, xmin((srclen) + 1, size))\r
66 \r
67 #define xmin(a, b) ((a) < (b) ? (a) : (b))\r
68 #define xmax(a, b) ((a) > (b) ? (a) : (b))\r
69 \r
70 #define STATIC_STRLEN(arr) (sizeof(arr) - 1)\r
71 \r
72 typedef unsigned char uchar;\r
73 typedef unsigned short ushort;\r
74 typedef unsigned int uint;\r
75 \r
76 typedef void* iconv_t;\r
77 \r
78 iconv_t iconv_open(const char *tocode, const char *fromcode);\r
79 int iconv_close(iconv_t cd);\r
80 size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
81 \r
82 /* libiconv interface for vim */\r
83 #if defined(MAKE_DLL)\r
84 int\r
85 iconvctl (iconv_t cd, int request, void* argument)\r
86 {\r
87     /* not supported */\r
88     return 0;\r
89 }\r
90 #endif\r
91 \r
92 typedef struct compat_t compat_t;\r
93 typedef struct csconv_t csconv_t;\r
94 typedef struct rec_iconv_t rec_iconv_t;\r
95 \r
96 typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode);\r
97 typedef int (*f_iconv_close)(iconv_t cd);\r
98 typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
99 typedef int* (*f_errno)(void);\r
100 typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
101 typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
102 typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize);\r
103 typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize);\r
104 \r
105 #define COMPAT_IN   1\r
106 #define COMPAT_OUT  2\r
107 \r
108 /* unicode mapping for compatibility with other conversion table. */\r
109 struct compat_t {\r
110     uint in;\r
111     uint out;\r
112     uint flag;\r
113 };\r
114 \r
115 struct csconv_t {\r
116     int codepage;\r
117     int flags;\r
118     f_mbtowc mbtowc;\r
119     f_wctomb wctomb;\r
120     f_mblen mblen;\r
121     f_flush flush;\r
122     DWORD mode;\r
123     compat_t *compat;\r
124 };\r
125 \r
126 struct rec_iconv_t {\r
127     iconv_t cd;\r
128     f_iconv_close iconv_close;\r
129     f_iconv iconv;\r
130     f_errno _errno;\r
131     csconv_t from;\r
132     csconv_t to;\r
133 #if defined(USE_LIBICONV_DLL)\r
134     HMODULE hlibiconv;\r
135 #endif\r
136 };\r
137 \r
138 static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);\r
139 static int win_iconv_close(iconv_t cd);\r
140 static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
141 \r
142 static int load_mlang();\r
143 static csconv_t make_csconv(const char *name);\r
144 static int name_to_codepage(const char *name);\r
145 static uint utf16_to_ucs4(const ushort *wbuf);\r
146 static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize);\r
147 static int is_unicode(int codepage);\r
148 static int mbtowc_flags(int codepage);\r
149 static int must_use_null_useddefaultchar(int codepage);\r
150 static void check_utf_bom(rec_iconv_t *cd, ushort *wbuf, int *wbufsize);\r
151 static char *strrstr(const char *str, const char *token);\r
152 \r
153 #if defined(USE_LIBICONV_DLL)\r
154 static int libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);\r
155 static PVOID MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size);\r
156 static HMODULE find_imported_module_by_funcname(HMODULE hModule, const char *funcname);\r
157 \r
158 static HMODULE hwiniconv;\r
159 static HMODULE hlastdll; /* keep dll loaded for efficiency (unnecessary?) */\r
160 #endif\r
161 \r
162 static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
163 static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
164 static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
165 static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
166 static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
167 \r
168 static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
169 static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
170 static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
171 static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
172 static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
173 static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
174 static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
175 static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
176 static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
177 static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
178 static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize);\r
179 \r
180 static struct {\r
181     int codepage;\r
182     const char *name;\r
183 } codepage_alias[] = {\r
184     {65001, "CP65001"},\r
185     {65001, "UTF8"},\r
186     {65001, "UTF-8"},\r
187 \r
188     {1200, "CP1200"},\r
189     {1200, "UTF16LE"},\r
190     {1200, "UTF-16LE"},\r
191     {1200, "UCS2LE"},\r
192     {1200, "UCS-2LE"},\r
193 \r
194     {1201, "CP1201"},\r
195     {1201, "UTF16BE"},\r
196     {1201, "UTF-16BE"},\r
197     {1201, "UCS2BE"},\r
198     {1201, "UCS-2BE"},\r
199     {1201, "unicodeFFFE"},\r
200 \r
201     {12000, "CP12000"},\r
202     {12000, "UTF32LE"},\r
203     {12000, "UTF-32LE"},\r
204     {12000, "UCS4LE"},\r
205     {12000, "UCS-4LE"},\r
206 \r
207     {12001, "CP12001"},\r
208     {12001, "UTF32BE"},\r
209     {12001, "UTF-32BE"},\r
210     {12001, "UCS4BE"},\r
211     {12001, "UCS-4BE"},\r
212 \r
213 #ifndef GLIB_COMPILATION\r
214     /*\r
215      * Default is big endian.\r
216      * See rfc2781 4.3 Interpreting text labelled as UTF-16.\r
217      */\r
218     {1201, "UTF16"},\r
219     {1201, "UTF-16"},\r
220     {12001, "UTF32"},\r
221     {12001, "UTF-32"},\r
222     {12001, "UCS-4"},\r
223     {12001, "UCS4"},\r
224 #else\r
225     /* Default is little endian, because the platform is */\r
226     {1200, "UTF16"},\r
227     {1200, "UTF-16"},\r
228     {1200, "UCS2"},\r
229     {1200, "UCS-2"},\r
230     {12000, "UTF32"},\r
231     {12000, "UTF-32"},\r
232     {12000, "UCS4"},\r
233     {12000, "UCS-4"},\r
234 #endif\r
235 \r
236     /* copy from libiconv `iconv -l` */\r
237     /* !IsValidCodePage(367) */\r
238     {20127, "ANSI_X3.4-1968"},\r
239     {20127, "ANSI_X3.4-1986"},\r
240     {20127, "ASCII"},\r
241     {20127, "CP367"},\r
242     {20127, "IBM367"},\r
243     {20127, "ISO-IR-6"},\r
244     {20127, "ISO646-US"},\r
245     {20127, "ISO_646.IRV:1991"},\r
246     {20127, "US"},\r
247     {20127, "US-ASCII"},\r
248     {20127, "CSASCII"},\r
249 \r
250     /* !IsValidCodePage(819) */\r
251     {1252, "CP819"},\r
252     {1252, "IBM819"},\r
253     {28591, "ISO-8859-1"},\r
254     {28591, "ISO-IR-100"},\r
255     {28591, "ISO8859-1"},\r
256     {28591, "ISO_8859-1"},\r
257     {28591, "ISO_8859-1:1987"},\r
258     {28591, "L1"},\r
259     {28591, "LATIN1"},\r
260     {28591, "CSISOLATIN1"},\r
261 \r
262     {1250, "CP1250"},\r
263     {1250, "MS-EE"},\r
264     {1250, "WINDOWS-1250"},\r
265 \r
266     {1251, "CP1251"},\r
267     {1251, "MS-CYRL"},\r
268     {1251, "WINDOWS-1251"},\r
269 \r
270     {1252, "CP1252"},\r
271     {1252, "MS-ANSI"},\r
272     {1252, "WINDOWS-1252"},\r
273 \r
274     {1253, "CP1253"},\r
275     {1253, "MS-GREEK"},\r
276     {1253, "WINDOWS-1253"},\r
277 \r
278     {1254, "CP1254"},\r
279     {1254, "MS-TURK"},\r
280     {1254, "WINDOWS-1254"},\r
281 \r
282     {1255, "CP1255"},\r
283     {1255, "MS-HEBR"},\r
284     {1255, "WINDOWS-1255"},\r
285 \r
286     {1256, "CP1256"},\r
287     {1256, "MS-ARAB"},\r
288     {1256, "WINDOWS-1256"},\r
289 \r
290     {1257, "CP1257"},\r
291     {1257, "WINBALTRIM"},\r
292     {1257, "WINDOWS-1257"},\r
293 \r
294     {1258, "CP1258"},\r
295     {1258, "WINDOWS-1258"},\r
296 \r
297     {850, "850"},\r
298     {850, "CP850"},\r
299     {850, "IBM850"},\r
300     {850, "CSPC850MULTILINGUAL"},\r
301 \r
302     /* !IsValidCodePage(862) */\r
303     {862, "862"},\r
304     {862, "CP862"},\r
305     {862, "IBM862"},\r
306     {862, "CSPC862LATINHEBREW"},\r
307 \r
308     {866, "866"},\r
309     {866, "CP866"},\r
310     {866, "IBM866"},\r
311     {866, "CSIBM866"},\r
312 \r
313     /* !IsValidCodePage(154) */\r
314     {154, "CP154"},\r
315     {154, "CYRILLIC-ASIAN"},\r
316     {154, "PT154"},\r
317     {154, "PTCP154"},\r
318     {154, "CSPTCP154"},\r
319 \r
320     /* !IsValidCodePage(1133) */\r
321     {1133, "CP1133"},\r
322     {1133, "IBM-CP1133"},\r
323 \r
324     {874, "CP874"},\r
325     {874, "WINDOWS-874"},\r
326 \r
327     /* !IsValidCodePage(51932) */\r
328     {51932, "CP51932"},\r
329     {51932, "MS51932"},\r
330     {51932, "WINDOWS-51932"},\r
331     {51932, "EUC-JP"},\r
332 \r
333     {932, "CP932"},\r
334     {932, "MS932"},\r
335     {932, "SHIFFT_JIS"},\r
336     {932, "SHIFFT_JIS-MS"},\r
337     {932, "SJIS"},\r
338     {932, "SJIS-MS"},\r
339     {932, "SJIS-OPEN"},\r
340     {932, "SJIS-WIN"},\r
341     {932, "WINDOWS-31J"},\r
342     {932, "WINDOWS-932"},\r
343     {932, "CSWINDOWS31J"},\r
344 \r
345     {50221, "CP50221"},\r
346     {50221, "ISO-2022-JP"},\r
347     {50221, "ISO-2022-JP-MS"},\r
348     {50221, "ISO2022-JP"},\r
349     {50221, "ISO2022-JP-MS"},\r
350     {50221, "MS50221"},\r
351     {50221, "WINDOWS-50221"},\r
352 \r
353     {936, "CP936"},\r
354     {936, "GBK"},\r
355     {936, "MS936"},\r
356     {936, "WINDOWS-936"},\r
357 \r
358     {950, "CP950"},\r
359     {950, "BIG5"},\r
360 \r
361     {949, "CP949"},\r
362     {949, "UHC"},\r
363     {949, "EUC-KR"},\r
364 \r
365     {1361, "CP1361"},\r
366     {1361, "JOHAB"},\r
367 \r
368     {437, "437"},\r
369     {437, "CP437"},\r
370     {437, "IBM437"},\r
371     {437, "CSPC8CODEPAGE437"},\r
372 \r
373     {737, "CP737"},\r
374 \r
375     {775, "CP775"},\r
376     {775, "IBM775"},\r
377     {775, "CSPC775BALTIC"},\r
378 \r
379     {852, "852"},\r
380     {852, "CP852"},\r
381     {852, "IBM852"},\r
382     {852, "CSPCP852"},\r
383 \r
384     /* !IsValidCodePage(853) */\r
385     {853, "CP853"},\r
386 \r
387     {855, "855"},\r
388     {855, "CP855"},\r
389     {855, "IBM855"},\r
390     {855, "CSIBM855"},\r
391 \r
392     {857, "857"},\r
393     {857, "CP857"},\r
394     {857, "IBM857"},\r
395     {857, "CSIBM857"},\r
396 \r
397     /* !IsValidCodePage(858) */\r
398     {858, "CP858"},\r
399 \r
400     {860, "860"},\r
401     {860, "CP860"},\r
402     {860, "IBM860"},\r
403     {860, "CSIBM860"},\r
404 \r
405     {861, "861"},\r
406     {861, "CP-IS"},\r
407     {861, "CP861"},\r
408     {861, "IBM861"},\r
409     {861, "CSIBM861"},\r
410 \r
411     {863, "863"},\r
412     {863, "CP863"},\r
413     {863, "IBM863"},\r
414     {863, "CSIBM863"},\r
415 \r
416     {864, "CP864"},\r
417     {864, "IBM864"},\r
418     {864, "CSIBM864"},\r
419 \r
420     {865, "865"},\r
421     {865, "CP865"},\r
422     {865, "IBM865"},\r
423     {865, "CSIBM865"},\r
424 \r
425     {869, "869"},\r
426     {869, "CP-GR"},\r
427     {869, "CP869"},\r
428     {869, "IBM869"},\r
429     {869, "CSIBM869"},\r
430 \r
431     /* !IsValidCodePage(1152) */\r
432     {1125, "CP1125"},\r
433 \r
434     /*\r
435      * Code Page Identifiers\r
436      * http://msdn2.microsoft.com/en-us/library/ms776446.aspx\r
437      */\r
438     {37, "IBM037"}, /* IBM EBCDIC US-Canada */\r
439     {437, "IBM437"}, /* OEM United States */\r
440     {500, "IBM500"}, /* IBM EBCDIC International */\r
441     {708, "ASMO-708"}, /* Arabic (ASMO 708) */\r
442     /* 709              Arabic (ASMO-449+, BCON V4) */\r
443     /* 710              Arabic - Transparent Arabic */\r
444     {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */\r
445     {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */\r
446     {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */\r
447     {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */\r
448     {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */\r
449     {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */\r
450     {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */\r
451     {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */\r
452     {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */\r
453     {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */\r
454     {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */\r
455     {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */\r
456     {864, "IBM864"}, /* OEM Arabic; Arabic (864) */\r
457     {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */\r
458     {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */\r
459     {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */\r
460     {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */\r
461     {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */\r
462     {875, "cp875"}, /* IBM EBCDIC Greek Modern */\r
463     {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */\r
464     {932, "shift-jis"}, /* alternative name for it */\r
465     {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */\r
466     {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */\r
467     {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */\r
468     {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */\r
469     {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */\r
470     {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */\r
471     {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */\r
472     {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */\r
473     {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */\r
474     {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */\r
475     {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */\r
476     {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */\r
477     {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */\r
478     {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */\r
479     {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */\r
480     {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */\r
481     {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */\r
482     {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */\r
483     {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */\r
484     {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */\r
485     {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */\r
486     {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */\r
487     {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */\r
488     {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */\r
489     {1361, "Johab"}, /* Korean (Johab) */\r
490     {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */\r
491     {10001, "x-mac-japanese"}, /* Japanese (Mac) */\r
492     {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */\r
493     {10003, "x-mac-korean"}, /* Korean (Mac) */\r
494     {10004, "x-mac-arabic"}, /* Arabic (Mac) */\r
495     {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */\r
496     {10006, "x-mac-greek"}, /* Greek (Mac) */\r
497     {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */\r
498     {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */\r
499     {10010, "x-mac-romanian"}, /* Romanian (Mac) */\r
500     {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */\r
501     {10021, "x-mac-thai"}, /* Thai (Mac) */\r
502     {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */\r
503     {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */\r
504     {10081, "x-mac-turkish"}, /* Turkish (Mac) */\r
505     {10082, "x-mac-croatian"}, /* Croatian (Mac) */\r
506     {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */\r
507     {20001, "x-cp20001"}, /* TCA Taiwan */\r
508     {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */\r
509     {20003, "x-cp20003"}, /* IBM5550 Taiwan */\r
510     {20004, "x-cp20004"}, /* TeleText Taiwan */\r
511     {20005, "x-cp20005"}, /* Wang Taiwan */\r
512     {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */\r
513     {20106, "x-IA5-German"}, /* IA5 German (7-bit) */\r
514     {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */\r
515     {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */\r
516     {20127, "us-ascii"}, /* US-ASCII (7-bit) */\r
517     {20261, "x-cp20261"}, /* T.61 */\r
518     {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */\r
519     {20273, "IBM273"}, /* IBM EBCDIC Germany */\r
520     {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */\r
521     {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */\r
522     {20280, "IBM280"}, /* IBM EBCDIC Italy */\r
523     {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */\r
524     {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */\r
525     {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */\r
526     {20297, "IBM297"}, /* IBM EBCDIC France */\r
527     {20420, "IBM420"}, /* IBM EBCDIC Arabic */\r
528     {20423, "IBM423"}, /* IBM EBCDIC Greek */\r
529     {20424, "IBM424"}, /* IBM EBCDIC Hebrew */\r
530     {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */\r
531     {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */\r
532     {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */\r
533     {20871, "IBM871"}, /* IBM EBCDIC Icelandic */\r
534     {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */\r
535     {20905, "IBM905"}, /* IBM EBCDIC Turkish */\r
536     {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */\r
537     {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */\r
538     {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */\r
539     {20949, "x-cp20949"}, /* Korean Wansung */\r
540     {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */\r
541     /* 21027            (deprecated) */\r
542     {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */\r
543     {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */\r
544     {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */\r
545     {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */\r
546     {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */\r
547     {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */\r
548     {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */\r
549     {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */\r
550     {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */\r
551     {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */\r
552     {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */\r
553     {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */\r
554     {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */\r
555     {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */\r
556     {28597, "iso8859-7"}, /* ISO 8859-7 Greek */\r
557     {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */\r
558     {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */\r
559     {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */\r
560     {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */\r
561     {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */\r
562     {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */\r
563     {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */\r
564     {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */\r
565     {29001, "x-Europa"}, /* Europa 3 */\r
566     {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */\r
567     {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */\r
568     {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */\r
569     {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */\r
570     {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */\r
571     {50225, "iso-2022-kr"}, /* ISO 2022 Korean */\r
572     {50225, "iso2022-kr"}, /* ISO 2022 Korean */\r
573     {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */\r
574     /* 50229            ISO 2022 Traditional Chinese */\r
575     /* 50930            EBCDIC Japanese (Katakana) Extended */\r
576     /* 50931            EBCDIC US-Canada and Japanese */\r
577     /* 50933            EBCDIC Korean Extended and Korean */\r
578     /* 50935            EBCDIC Simplified Chinese Extended and Simplified Chinese */\r
579     /* 50936            EBCDIC Simplified Chinese */\r
580     /* 50937            EBCDIC US-Canada and Traditional Chinese */\r
581     /* 50939            EBCDIC Japanese (Latin) Extended and Japanese */\r
582     {51932, "euc-jp"}, /* EUC Japanese */\r
583     {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */\r
584     {51949, "euc-kr"}, /* EUC Korean */\r
585     /* 51950            EUC Traditional Chinese */\r
586     {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */\r
587     {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */\r
588     {57002, "x-iscii-de"}, /* ISCII Devanagari */\r
589     {57003, "x-iscii-be"}, /* ISCII Bengali */\r
590     {57004, "x-iscii-ta"}, /* ISCII Tamil */\r
591     {57005, "x-iscii-te"}, /* ISCII Telugu */\r
592     {57006, "x-iscii-as"}, /* ISCII Assamese */\r
593     {57007, "x-iscii-or"}, /* ISCII Oriya */\r
594     {57008, "x-iscii-ka"}, /* ISCII Kannada */\r
595     {57009, "x-iscii-ma"}, /* ISCII Malayalam */\r
596     {57010, "x-iscii-gu"}, /* ISCII Gujarati */\r
597     {57011, "x-iscii-pa"}, /* ISCII Punjabi */\r
598 \r
599     {0, NULL}\r
600 };\r
601 \r
602 /*\r
603  * SJIS SHIFTJIS table              CP932 table\r
604  * ---- --------------------------- --------------------------------\r
605  *   5C U+00A5 YEN SIGN             U+005C REVERSE SOLIDUS\r
606  *   7E U+203E OVERLINE             U+007E TILDE\r
607  * 815C U+2014 EM DASH              U+2015 HORIZONTAL BAR\r
608  * 815F U+005C REVERSE SOLIDUS      U+FF3C FULLWIDTH REVERSE SOLIDUS\r
609  * 8160 U+301C WAVE DASH            U+FF5E FULLWIDTH TILDE\r
610  * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO\r
611  * 817C U+2212 MINUS SIGN           U+FF0D FULLWIDTH HYPHEN-MINUS\r
612  * 8191 U+00A2 CENT SIGN            U+FFE0 FULLWIDTH CENT SIGN\r
613  * 8192 U+00A3 POUND SIGN           U+FFE1 FULLWIDTH POUND SIGN\r
614  * 81CA U+00AC NOT SIGN             U+FFE2 FULLWIDTH NOT SIGN\r
615  *\r
616  * EUC-JP and ISO-2022-JP should be compatible with CP932.\r
617  *\r
618  * Kernel and MLang have different Unicode mapping table.  Make sure\r
619  * which API is used.\r
620  */\r
621 static compat_t cp932_compat[] = {\r
622     {0x00A5, 0x005C, COMPAT_OUT},\r
623     {0x203E, 0x007E, COMPAT_OUT},\r
624     {0x2014, 0x2015, COMPAT_OUT},\r
625     {0x301C, 0xFF5E, COMPAT_OUT},\r
626     {0x2016, 0x2225, COMPAT_OUT},\r
627     {0x2212, 0xFF0D, COMPAT_OUT},\r
628     {0x00A2, 0xFFE0, COMPAT_OUT},\r
629     {0x00A3, 0xFFE1, COMPAT_OUT},\r
630     {0x00AC, 0xFFE2, COMPAT_OUT},\r
631     {0, 0, 0}\r
632 };\r
633 \r
634 static compat_t cp20932_compat[] = {\r
635     {0x00A5, 0x005C, COMPAT_OUT},\r
636     {0x203E, 0x007E, COMPAT_OUT},\r
637     {0x2014, 0x2015, COMPAT_OUT},\r
638     {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN},\r
639     {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN},\r
640     {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN},\r
641     {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN},\r
642     {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN},\r
643     {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN},\r
644     {0, 0, 0}\r
645 };\r
646 \r
647 static compat_t *cp51932_compat = cp932_compat;\r
648 \r
649 /* cp20932_compat for kernel.  cp932_compat for mlang. */\r
650 static compat_t *cp5022x_compat = cp932_compat;\r
651 \r
652 typedef HRESULT (WINAPI *CONVERTINETSTRING)(\r
653     LPDWORD lpdwMode,\r
654     DWORD dwSrcEncoding,\r
655     DWORD dwDstEncoding,\r
656     LPCSTR lpSrcStr,\r
657     LPINT lpnSrcSize,\r
658     LPBYTE lpDstStr,\r
659     LPINT lpnDstSize\r
660 );\r
661 typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)(\r
662     LPDWORD lpdwMode,\r
663     DWORD dwSrcEncoding,\r
664     LPCSTR lpSrcStr,\r
665     LPINT lpnMultiCharCount,\r
666     LPWSTR lpDstStr,\r
667     LPINT lpnWideCharCount\r
668 );\r
669 typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)(\r
670     LPDWORD lpdwMode,\r
671     DWORD dwEncoding,\r
672     LPCWSTR lpSrcStr,\r
673     LPINT lpnWideCharCount,\r
674     LPSTR lpDstStr,\r
675     LPINT lpnMultiCharCount\r
676 );\r
677 typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)(\r
678     DWORD dwSrcEncoding,\r
679     DWORD dwDstEncoding\r
680 );\r
681 typedef HRESULT (WINAPI *LCIDTORFC1766A)(\r
682     LCID Locale,\r
683     LPSTR pszRfc1766,\r
684     int nChar\r
685 );\r
686 typedef HRESULT (WINAPI *LCIDTORFC1766W)(\r
687     LCID Locale,\r
688     LPWSTR pszRfc1766,\r
689     int nChar\r
690 );\r
691 typedef HRESULT (WINAPI *RFC1766TOLCIDA)(\r
692     LCID *pLocale,\r
693     LPSTR pszRfc1766\r
694 );\r
695 typedef HRESULT (WINAPI *RFC1766TOLCIDW)(\r
696     LCID *pLocale,\r
697     LPWSTR pszRfc1766\r
698 );\r
699 static CONVERTINETSTRING ConvertINetString;\r
700 static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode;\r
701 static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte;\r
702 static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable;\r
703 static LCIDTORFC1766A LcidToRfc1766A;\r
704 static RFC1766TOLCIDA Rfc1766ToLcidA;\r
705 \r
706 static int\r
707 load_mlang()\r
708 {\r
709     HMODULE h = NULL;\r
710     char mlang_dll[MAX_PATH + 100];\r
711     int n;\r
712     if (ConvertINetString != NULL)\r
713         return TRUE;\r
714     n = GetSystemDirectory(mlang_dll, MAX_PATH);\r
715     if (n > 0 && n < MAX_PATH)\r
716     {\r
717         if (mlang_dll[n-1] != '\\' &&\r
718             mlang_dll[n-1] != '/')\r
719             strcat(mlang_dll, "\\");\r
720         strcat(mlang_dll, "mlang.dll");\r
721         h = LoadLibrary(mlang_dll);\r
722     }\r
723     if (!h)\r
724         return FALSE;\r
725     ConvertINetString = (CONVERTINETSTRING)GetProcAddress(h, "ConvertINetString");\r
726     ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddress(h, "ConvertINetMultiByteToUnicode");\r
727     ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddress(h, "ConvertINetUnicodeToMultiByte");\r
728     IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddress(h, "IsConvertINetStringAvailable");\r
729     LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddress(h, "LcidToRfc1766A");\r
730     Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddress(h, "Rfc1766ToLcidA");\r
731     return TRUE;\r
732 }\r
733 \r
734 iconv_t\r
735 iconv_open(const char *tocode, const char *fromcode)\r
736 {\r
737     rec_iconv_t *cd;\r
738 \r
739     cd = (rec_iconv_t *)calloc(1, sizeof(rec_iconv_t));\r
740     if (cd == NULL)\r
741     {\r
742         errno = ENOMEM;\r
743         return (iconv_t)(-1);\r
744     }\r
745 \r
746 #if defined(USE_LIBICONV_DLL)\r
747     if (libiconv_iconv_open(cd, tocode, fromcode))\r
748         return (iconv_t)cd;\r
749 #endif\r
750 \r
751     if (win_iconv_open(cd, tocode, fromcode))\r
752         return (iconv_t)cd;\r
753 \r
754     free(cd);\r
755     errno = EINVAL;\r
756     return (iconv_t)(-1);\r
757 }\r
758 \r
759 int\r
760 iconv_close(iconv_t _cd)\r
761 {\r
762     rec_iconv_t *cd = (rec_iconv_t *)_cd;\r
763     int r = cd->iconv_close(cd->cd);\r
764     int e = *(cd->_errno());\r
765 #if defined(USE_LIBICONV_DLL)\r
766     if (cd->hlibiconv != NULL)\r
767         FreeLibrary(cd->hlibiconv);\r
768 #endif\r
769     free(cd);\r
770     errno = e;\r
771     return r;\r
772 }\r
773 \r
774 size_t\r
775 iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)\r
776 {\r
777     rec_iconv_t *cd = (rec_iconv_t *)_cd;\r
778     size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft);\r
779     errno = *(cd->_errno());\r
780     return r;\r
781 }\r
782 \r
783 static int\r
784 win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode)\r
785 {\r
786     cd->from = make_csconv(fromcode);\r
787     cd->to = make_csconv(tocode);\r
788     if (cd->from.codepage == -1 || cd->to.codepage == -1)\r
789         return FALSE;\r
790     cd->iconv_close = win_iconv_close;\r
791     cd->iconv = win_iconv;\r
792     cd->_errno = _errno;\r
793     cd->cd = (iconv_t)cd;\r
794     return TRUE;\r
795 }\r
796 \r
797 static int\r
798 win_iconv_close(iconv_t cd)\r
799 {\r
800     return 0;\r
801 }\r
802 \r
803 static size_t\r
804 win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)\r
805 {\r
806     rec_iconv_t *cd = (rec_iconv_t *)_cd;\r
807     ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */\r
808     int insize;\r
809     int outsize;\r
810     int wsize;\r
811     DWORD mode;\r
812     uint wc;\r
813     compat_t *cp;\r
814     int i;\r
815 \r
816     if (inbuf == NULL || *inbuf == NULL)\r
817     {\r
818         if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL)\r
819         {\r
820             outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft);\r
821             if (outsize == -1)\r
822                 return (size_t)(-1);\r
823             *outbuf += outsize;\r
824             *outbytesleft -= outsize;\r
825         }\r
826         if (is_unicode(cd->from.codepage) && (cd->from.mode & UNICODE_MODE_SWAPPED))\r
827             cd->from.codepage ^= 1;\r
828         cd->from.mode = 0;\r
829         cd->to.mode = 0;\r
830         return 0;\r
831     }\r
832 \r
833     while (*inbytesleft != 0)\r
834     {\r
835         mode = cd->from.mode;\r
836         wsize = MB_CHAR_MAX;\r
837 \r
838         insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize);\r
839         if (insize == -1)\r
840             return (size_t)(-1);\r
841 \r
842         if (is_unicode(cd->from.codepage) && !(cd->from.mode & UNICODE_MODE_BOM_DONE))\r
843         {\r
844             check_utf_bom(cd, wbuf, &wsize);\r
845             cd->from.mode |= UNICODE_MODE_BOM_DONE;\r
846         }\r
847 \r
848         if (wsize == 0)\r
849         {\r
850             *inbuf += insize;\r
851             *inbytesleft -= insize;\r
852             continue;\r
853         }\r
854 \r
855         if (cd->from.compat != NULL)\r
856         {\r
857             wc = utf16_to_ucs4(wbuf);\r
858             cp = cd->from.compat;\r
859             for (i = 0; cp[i].in != 0; ++i)\r
860             {\r
861                 if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc)\r
862                 {\r
863                     ucs4_to_utf16(cp[i].in, wbuf, &wsize);\r
864                     break;\r
865                 }\r
866             }\r
867         }\r
868 \r
869         if (cd->to.compat != NULL)\r
870         {\r
871             wc = utf16_to_ucs4(wbuf);\r
872             cp = cd->to.compat;\r
873             for (i = 0; cp[i].in != 0; ++i)\r
874             {\r
875                 if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc)\r
876                 {\r
877                     ucs4_to_utf16(cp[i].out, wbuf, &wsize);\r
878                     break;\r
879                 }\r
880             }\r
881         }\r
882 \r
883         outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft);\r
884         if (outsize == -1)\r
885         {\r
886             cd->from.mode = mode;\r
887             return (size_t)(-1);\r
888         }\r
889 \r
890         *inbuf += insize;\r
891         *outbuf += outsize;\r
892         *inbytesleft -= insize;\r
893         *outbytesleft -= outsize;\r
894     }\r
895 \r
896     return 0;\r
897 }\r
898 \r
899 static csconv_t\r
900 make_csconv(const char *_name)\r
901 {\r
902     CPINFOEX cpinfoex;\r
903     csconv_t cv;\r
904     int use_compat = TRUE;\r
905     int flag = 0;\r
906     char name[128];\r
907     char *p;\r
908 \r
909     xstrlcpy(name, _name, sizeof(name));\r
910 \r
911     /* check for option "enc_name//opt1//opt2" */\r
912     while ((p = strrstr(name, "//")) != NULL)\r
913     {\r
914         if (_stricmp(p + 2, "nocompat") == 0)\r
915             use_compat = FALSE;\r
916         else if (_stricmp(p + 2, "translit") == 0)\r
917             flag |= FLAG_TRANSLIT;\r
918         else if (_stricmp(p + 2, "ignore") == 0)\r
919             flag |= FLAG_IGNORE;\r
920         *p = 0;\r
921     }\r
922 \r
923     cv.mode = 0;\r
924     cv.flags = flag;\r
925     cv.mblen = NULL;\r
926     cv.flush = NULL;\r
927     cv.compat = NULL;\r
928     cv.codepage = name_to_codepage(name);\r
929     if (cv.codepage == 1200 || cv.codepage == 1201)\r
930     {\r
931         cv.mbtowc = utf16_mbtowc;\r
932         cv.wctomb = utf16_wctomb;\r
933         if (_stricmp(name, "UTF-16") == 0 ||\r
934             _stricmp(name, "UTF16") == 0 ||\r
935             _stricmp(name, "UCS-2") == 0 ||\r
936             _stricmp(name, "UCS2") == 0)\r
937             cv.flags |= FLAG_USE_BOM_ENDIAN;\r
938     }\r
939     else if (cv.codepage == 12000 || cv.codepage == 12001)\r
940     {\r
941         cv.mbtowc = utf32_mbtowc;\r
942         cv.wctomb = utf32_wctomb;\r
943         if (_stricmp(name, "UTF-32") == 0 ||\r
944             _stricmp(name, "UTF32") == 0 ||\r
945             _stricmp(name, "UCS-4") == 0 ||\r
946             _stricmp(name, "UCS4") == 0)\r
947             cv.flags |= FLAG_USE_BOM_ENDIAN;\r
948     }\r
949     else if (cv.codepage == 65001)\r
950     {\r
951         cv.mbtowc = kernel_mbtowc;\r
952         cv.wctomb = kernel_wctomb;\r
953         cv.mblen = utf8_mblen;\r
954     }\r
955     else if ((cv.codepage == 50220 || cv.codepage == 50221 || cv.codepage == 50222) && load_mlang())\r
956     {\r
957         cv.mbtowc = iso2022jp_mbtowc;\r
958         cv.wctomb = iso2022jp_wctomb;\r
959         cv.flush = iso2022jp_flush;\r
960     }\r
961     else if (cv.codepage == 51932 && load_mlang())\r
962     {\r
963         cv.mbtowc = mlang_mbtowc;\r
964         cv.wctomb = mlang_wctomb;\r
965         cv.mblen = eucjp_mblen;\r
966     }\r
967     else if (IsValidCodePage(cv.codepage)\r
968              && GetCPInfoEx(cv.codepage, 0, &cpinfoex) != 0)\r
969     {\r
970         cv.mbtowc = kernel_mbtowc;\r
971         cv.wctomb = kernel_wctomb;\r
972         if (cpinfoex.MaxCharSize == 1)\r
973             cv.mblen = sbcs_mblen;\r
974         else if (cpinfoex.MaxCharSize == 2)\r
975             cv.mblen = dbcs_mblen;\r
976         else\r
977             cv.mblen = mbcs_mblen;\r
978     }\r
979     else\r
980     {\r
981         /* not supported */\r
982         cv.codepage = -1;\r
983     }\r
984     if (use_compat)\r
985     {\r
986         switch (cv.codepage)\r
987         {\r
988         case 932: cv.compat = cp932_compat; break;\r
989         case 20932: cv.compat = cp20932_compat; break;\r
990         case 51932: cv.compat = cp51932_compat; break;\r
991         case 50220: case 50221: case 50222: cv.compat = cp5022x_compat; break;\r
992         }\r
993     }\r
994     return cv;\r
995 }\r
996 \r
997 static int\r
998 name_to_codepage(const char *name)\r
999 {\r
1000     int i;\r
1001 \r
1002     if (*name == '\0' ||\r
1003         strcmp(name, "char") == 0)\r
1004         return GetACP();\r
1005     else if (strcmp(name, "wchar_t") == 0)\r
1006         return 1200;\r
1007     else if (_strnicmp(name, "cp", 2) == 0)\r
1008         return atoi(name + 2); /* CP123 */\r
1009     else if ('0' <= name[0] && name[0] <= '9')\r
1010         return atoi(name);     /* 123 */\r
1011     else if (_strnicmp(name, "xx", 2) == 0)\r
1012         return atoi(name + 2); /* XX123 for debug */\r
1013 \r
1014     for (i = 0; codepage_alias[i].name != NULL; ++i)\r
1015         if (_stricmp(name, codepage_alias[i].name) == 0)\r
1016             return codepage_alias[i].codepage;\r
1017     return -1;\r
1018 }\r
1019 \r
1020 /*\r
1021  * http://www.faqs.org/rfcs/rfc2781.html\r
1022  */\r
1023 static uint\r
1024 utf16_to_ucs4(const ushort *wbuf)\r
1025 {\r
1026     uint wc = wbuf[0];\r
1027     if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)\r
1028         wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000;\r
1029     return wc;\r
1030 }\r
1031 \r
1032 static void\r
1033 ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize)\r
1034 {\r
1035     if (wc < 0x10000)\r
1036     {\r
1037         wbuf[0] = wc;\r
1038         *wbufsize = 1;\r
1039     }\r
1040     else\r
1041     {\r
1042         wc -= 0x10000;\r
1043         wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF);\r
1044         wbuf[1] = 0xDC00 | (wc & 0x3FF);\r
1045         *wbufsize = 2;\r
1046     }\r
1047 }\r
1048 \r
1049 static int\r
1050 is_unicode(int codepage)\r
1051 {\r
1052     return (codepage == 1200 || codepage == 1201 ||\r
1053             codepage == 12000 || codepage == 12001 ||\r
1054             codepage == 65000 || codepage == 65001);\r
1055 }\r
1056 \r
1057 /*\r
1058  * Check if codepage is one of those for which the dwFlags parameter\r
1059  * to MultiByteToWideChar() must be zero. Return zero or\r
1060  * MB_ERR_INVALID_CHARS.  The docs in Platform SDK for for Windows\r
1061  * Server 2003 R2 claims that also codepage 65001 is one of these, but\r
1062  * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave\r
1063  * out 65001 (UTF-8), and that indeed seems to be the case on XP, it\r
1064  * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting\r
1065  * from UTF-8.\r
1066  */\r
1067 static int\r
1068 mbtowc_flags(int codepage)\r
1069 {\r
1070     return (codepage == 50220 || codepage == 50221 ||\r
1071             codepage == 50222 || codepage == 50225 ||\r
1072             codepage == 50227 || codepage == 50229 ||\r
1073             codepage == 52936 || codepage == 54936 ||\r
1074             (codepage >= 57002 && codepage <= 57011) ||\r
1075             codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS;\r
1076 }\r
1077 \r
1078 /*\r
1079  * Check if codepage is one those for which the lpUsedDefaultChar\r
1080  * parameter to WideCharToMultiByte() must be NULL.  The docs in\r
1081  * Platform SDK for for Windows Server 2003 R2 claims that this is the\r
1082  * list below, while the MSDN docs for MSVS2008 claim that it is only\r
1083  * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform\r
1084  * SDK seems to be correct, at least for XP.\r
1085  */\r
1086 static int\r
1087 must_use_null_useddefaultchar(int codepage)\r
1088 {\r
1089     return (codepage == 65000 || codepage == 65001 ||\r
1090             codepage == 50220 || codepage == 50221 ||\r
1091             codepage == 50222 || codepage == 50225 ||\r
1092             codepage == 50227 || codepage == 50229 ||\r
1093             codepage == 52936 || codepage == 54936 ||\r
1094             (codepage >= 57002 && codepage <= 57011) ||\r
1095             codepage == 42);\r
1096 }\r
1097 \r
1098 static void\r
1099 check_utf_bom(rec_iconv_t *cd, ushort *wbuf, int *wbufsize)\r
1100 {\r
1101     /* If we have a BOM, trust it, despite what the caller said */\r
1102     if (wbuf[0] == 0xFFFE && (cd->from.flags & FLAG_USE_BOM_ENDIAN))\r
1103     {\r
1104         /* swap endian: 1200 <-> 1201 or 12000 <-> 12001 */\r
1105         cd->from.codepage ^= 1;\r
1106         cd->from.mode |= UNICODE_MODE_SWAPPED;\r
1107         wbuf[0] = 0xFEFF;\r
1108     }\r
1109 \r
1110     /*\r
1111      * Remove BOM.\r
1112      * Don't do this if "to" is Unicode,\r
1113      * except if "to" is UTF-8.\r
1114      */\r
1115     if (wbuf[0] == 0xFEFF && (!is_unicode(cd->to.codepage) || cd->to.codepage == 65001))\r
1116         *wbufsize = 0;\r
1117 }\r
1118 \r
1119 static char *\r
1120 strrstr(const char *str, const char *token)\r
1121 {\r
1122     int len = strlen(token);\r
1123     const char *p = str + strlen(str);\r
1124 \r
1125     while (str <= --p)\r
1126         if (p[0] == token[0] && strncmp(p, token, len) == 0)\r
1127             return (char *)p;\r
1128     return NULL;\r
1129 }\r
1130 \r
1131 #if defined(USE_LIBICONV_DLL)\r
1132 static int\r
1133 libiconv_iconv_open(rec_iconv_t *cd, const char *fromcode, const char *tocode)\r
1134 {\r
1135     HMODULE hlibiconv = NULL;\r
1136     HMODULE hmsvcrt = NULL;\r
1137     char dllname[_MAX_PATH];\r
1138     const char *p;\r
1139     const char *e;\r
1140     f_iconv_open _iconv_open;\r
1141 \r
1142     /*\r
1143      * always try to load dll, so that we can switch dll in runtime.\r
1144      */\r
1145 \r
1146     /* XXX: getenv() can't get variable set by SetEnvironmentVariable() */\r
1147     p = getenv("WINICONV_LIBICONV_DLL");\r
1148     if (p == NULL)\r
1149         p = DEFAULT_LIBICONV_DLL;\r
1150     /* parse comma separated value */\r
1151     for ( ; *p != 0; p = (*e == ',') ? e + 1 : e)\r
1152     {\r
1153         e = strchr(p, ',');\r
1154         if (p == e)\r
1155             continue;\r
1156         else if (e == NULL)\r
1157             e = p + strlen(p);\r
1158         xstrlcpyn(dllname, p, e - p, sizeof(dllname));\r
1159         hlibiconv = LoadLibrary(dllname);\r
1160         if (hlibiconv != NULL)\r
1161         {\r
1162             if (hlibiconv == hwiniconv)\r
1163             {\r
1164                 FreeLibrary(hlibiconv);\r
1165                 hlibiconv = NULL;\r
1166                 continue;\r
1167             }\r
1168             break;\r
1169         }\r
1170     }\r
1171 \r
1172     if (hlastdll != NULL)\r
1173     {\r
1174         /* decrement reference count */\r
1175         FreeLibrary(hlastdll);\r
1176         hlastdll = NULL;\r
1177     }\r
1178 \r
1179     if (hlibiconv == NULL)\r
1180         goto failed;\r
1181 \r
1182     hmsvcrt = find_imported_module_by_funcname(hlibiconv, "_errno");\r
1183     if (hmsvcrt == NULL)\r
1184         goto failed;\r
1185 \r
1186     _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "libiconv_open");\r
1187     if (_iconv_open == NULL)\r
1188         _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "iconv_open");\r
1189     cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "libiconv_close");\r
1190     if (cd->iconv_close == NULL)\r
1191         cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "iconv_close");\r
1192     cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "libiconv");\r
1193     if (cd->iconv == NULL)\r
1194         cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "iconv");\r
1195     cd->_errno = (f_errno)GetProcAddress(hmsvcrt, "_errno");\r
1196     if (_iconv_open == NULL || cd->iconv_close == NULL\r
1197             || cd->iconv == NULL || cd->_errno == NULL)\r
1198         goto failed;\r
1199 \r
1200     /* increment reference count */\r
1201     hlastdll = LoadLibrary(dllname);\r
1202 \r
1203     cd->cd = _iconv_open(tocode, fromcode);\r
1204     if (cd->cd == (iconv_t)(-1))\r
1205         goto failed;\r
1206 \r
1207     cd->hlibiconv = hlibiconv;\r
1208     return TRUE;\r
1209 \r
1210 failed:\r
1211     if (hlibiconv != NULL)\r
1212         FreeLibrary(hlibiconv);\r
1213     /* do not free hmsvcrt which is obtained by GetModuleHandle() */\r
1214     return FALSE;\r
1215 }\r
1216 \r
1217 /*\r
1218  * Reference:\r
1219  * http://forums.belution.com/ja/vc/000/234/78s.shtml\r
1220  * http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html\r
1221  *\r
1222  * The formal way is\r
1223  *   imagehlp.h or dbghelp.h\r
1224  *   imagehlp.lib or dbghelp.lib\r
1225  *   ImageDirectoryEntryToData()\r
1226  */\r
1227 #define TO_DOS_HEADER(base) ((PIMAGE_DOS_HEADER)(base))\r
1228 #define TO_NT_HEADERS(base) ((PIMAGE_NT_HEADERS)((LPBYTE)(base) + TO_DOS_HEADER(base)->e_lfanew))\r
1229 static PVOID\r
1230 MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size)\r
1231 {\r
1232     /* TODO: MappedAsImage? */\r
1233     PIMAGE_DATA_DIRECTORY p;\r
1234     p = TO_NT_HEADERS(Base)->OptionalHeader.DataDirectory + DirectoryEntry;\r
1235     if (p->VirtualAddress == 0) {\r
1236       *Size = 0;\r
1237       return NULL;\r
1238     }\r
1239     *Size = p->Size;\r
1240     return (PVOID)((LPBYTE)Base + p->VirtualAddress);\r
1241 }\r
1242 \r
1243 static HMODULE\r
1244 find_imported_module_by_funcname(HMODULE hModule, const char *funcname)\r
1245 {\r
1246     DWORD Base;\r
1247     ULONG Size;\r
1248     PIMAGE_IMPORT_DESCRIPTOR Imp;\r
1249     PIMAGE_THUNK_DATA Name;         /* Import Name Table */\r
1250     PIMAGE_IMPORT_BY_NAME ImpName;\r
1251 \r
1252     Base = (DWORD)hModule;\r
1253     Imp = MyImageDirectoryEntryToData(\r
1254             (LPVOID)Base,\r
1255             TRUE,\r
1256             IMAGE_DIRECTORY_ENTRY_IMPORT,\r
1257             &Size);\r
1258     if (Imp == NULL)\r
1259         return NULL;\r
1260     for ( ; Imp->OriginalFirstThunk != 0; ++Imp)\r
1261     {\r
1262         Name = (PIMAGE_THUNK_DATA)(Base + Imp->OriginalFirstThunk);\r
1263         for ( ; Name->u1.Ordinal != 0; ++Name)\r
1264         {\r
1265             if (!IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal))\r
1266             {\r
1267                 ImpName = (PIMAGE_IMPORT_BY_NAME)\r
1268                     (Base + (DWORD)Name->u1.AddressOfData);\r
1269                 if (strcmp((char *)ImpName->Name, funcname) == 0)\r
1270                     return GetModuleHandle((char *)(Base + Imp->Name));\r
1271             }\r
1272         }\r
1273     }\r
1274     return NULL;\r
1275 }\r
1276 #endif\r
1277 \r
1278 static int\r
1279 sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
1280 {\r
1281     return 1;\r
1282 }\r
1283 \r
1284 static int\r
1285 dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
1286 {\r
1287     int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1;\r
1288     if (bufsize < len)\r
1289         return_error(EINVAL);\r
1290     return len;\r
1291 }\r
1292 \r
1293 static int\r
1294 mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
1295 {\r
1296     int len = 0;\r
1297 \r
1298     if (cv->codepage == 54936) {\r
1299         if (buf[0] <= 0x7F) len = 1;\r
1300         else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&\r
1301                  bufsize >= 2 &&\r
1302                  ((buf[1] >= 0x40 && buf[1] <= 0x7E) ||\r
1303                   (buf[1] >= 0x80 && buf[1] <= 0xFE))) len = 2;\r
1304         else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&\r
1305                  bufsize >= 4 &&\r
1306                  buf[1] >= 0x30 && buf[1] <= 0x39) len = 4;\r
1307         else\r
1308             return_error(EINVAL);\r
1309         return len;\r
1310     }\r
1311     else\r
1312         return_error(EINVAL);\r
1313 }\r
1314 \r
1315 static int\r
1316 utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
1317 {\r
1318     int len = 0;\r
1319 \r
1320     if (buf[0] < 0x80) len = 1;\r
1321     else if ((buf[0] & 0xE0) == 0xC0) len = 2;\r
1322     else if ((buf[0] & 0xF0) == 0xE0) len = 3;\r
1323     else if ((buf[0] & 0xF8) == 0xF0) len = 4;\r
1324     else if ((buf[0] & 0xFC) == 0xF8) len = 5;\r
1325     else if ((buf[0] & 0xFE) == 0xFC) len = 6;\r
1326 \r
1327     if (len == 0)\r
1328         return_error(EILSEQ);\r
1329     else if (bufsize < len)\r
1330         return_error(EINVAL);\r
1331     return len;\r
1332 }\r
1333 \r
1334 static int\r
1335 eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
1336 {\r
1337     if (buf[0] < 0x80) /* ASCII */\r
1338         return 1;\r
1339     else if (buf[0] == 0x8E) /* JIS X 0201 */\r
1340     {\r
1341         if (bufsize < 2)\r
1342             return_error(EINVAL);\r
1343         else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF))\r
1344             return_error(EILSEQ);\r
1345         return 2;\r
1346     }\r
1347     else if (buf[0] == 0x8F) /* JIS X 0212 */\r
1348     {\r
1349         if (bufsize < 3)\r
1350             return_error(EINVAL);\r
1351         else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE)\r
1352                 || !(0xA1 <= buf[2] && buf[2] <= 0xFE))\r
1353             return_error(EILSEQ);\r
1354         return 3;\r
1355     }\r
1356     else /* JIS X 0208 */\r
1357     {\r
1358         if (bufsize < 2)\r
1359             return_error(EINVAL);\r
1360         else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE)\r
1361                 || !(0xA1 <= buf[1] && buf[1] <= 0xFE))\r
1362             return_error(EILSEQ);\r
1363         return 2;\r
1364     }\r
1365 }\r
1366 \r
1367 static int\r
1368 kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
1369 {\r
1370     int len;\r
1371 \r
1372     len = cv->mblen(cv, buf, bufsize);\r
1373     if (len == -1)\r
1374         return -1;\r
1375     /* If converting from ASCII, reject 8bit\r
1376      * chars. MultiByteToWideChar() doesn't. Note that for ASCII we\r
1377      * know that the mblen function is sbcs_mblen() so len is 1.\r
1378      */\r
1379     if (cv->codepage == 20127 && buf[0] >= 0x80)\r
1380         return_error(EILSEQ);\r
1381     *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage),\r
1382             (const char *)buf, len, (wchar_t *)wbuf, *wbufsize);\r
1383     if (*wbufsize == 0)\r
1384         return_error(EILSEQ);\r
1385     return len;\r
1386 }\r
1387 \r
1388 static int\r
1389 kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
1390 {\r
1391     BOOL usedDefaultChar = 0;\r
1392     BOOL *p = NULL;\r
1393     int flags = 0;\r
1394     int len;\r
1395 \r
1396     if (bufsize == 0)\r
1397         return_error(E2BIG);\r
1398     if (!must_use_null_useddefaultchar(cv->codepage))\r
1399     {\r
1400         p = &usedDefaultChar;\r
1401 #ifdef WC_NO_BEST_FIT_CHARS\r
1402         if (!(cv->flags & FLAG_TRANSLIT))\r
1403             flags |= WC_NO_BEST_FIT_CHARS;\r
1404 #endif\r
1405     }\r
1406     len = WideCharToMultiByte(cv->codepage, flags,\r
1407             (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p);\r
1408     if (len == 0)\r
1409     {\r
1410         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)\r
1411             return_error(E2BIG);\r
1412         return_error(EILSEQ);\r
1413     }\r
1414     else if (usedDefaultChar)\r
1415         return_error(EILSEQ);\r
1416     else if (cv->mblen(cv, buf, len) != len) /* validate result */\r
1417         return_error(EILSEQ);\r
1418     return len;\r
1419 }\r
1420 \r
1421 /*\r
1422  * It seems that the mode (cv->mode) is fixnum.\r
1423  * For example, when converting iso-2022-jp(cp50221) to unicode:\r
1424  *      in ascii sequence: mode=0xC42C0000\r
1425  *   in jisx0208 sequence: mode=0xC42C0001\r
1426  * "C42C" is same for each convert session.\r
1427  * It should be: ((codepage-1)<<16)|state\r
1428  */\r
1429 static int\r
1430 mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
1431 {\r
1432     int len;\r
1433     int insize;\r
1434     HRESULT hr;\r
1435 \r
1436     len = cv->mblen(cv, buf, bufsize);\r
1437     if (len == -1)\r
1438         return -1;\r
1439     insize = len;\r
1440     hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage,\r
1441             (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize);\r
1442     if (hr != S_OK || insize != len)\r
1443         return_error(EILSEQ);\r
1444     return len;\r
1445 }\r
1446 \r
1447 static int\r
1448 mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
1449 {\r
1450     char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */\r
1451     int tmpsize = MB_CHAR_MAX;\r
1452     int insize = wbufsize;\r
1453     HRESULT hr;\r
1454 \r
1455     hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage,\r
1456             (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize);\r
1457     if (hr != S_OK || insize != wbufsize)\r
1458         return_error(EILSEQ);\r
1459     else if (bufsize < tmpsize)\r
1460         return_error(E2BIG);\r
1461     else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize)\r
1462         return_error(EILSEQ);\r
1463     memcpy(buf, tmpbuf, tmpsize);\r
1464     return tmpsize;\r
1465 }\r
1466 \r
1467 static int\r
1468 utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
1469 {\r
1470     if (bufsize < 2)\r
1471         return_error(EINVAL);\r
1472     if (cv->codepage == 1200) /* little endian */\r
1473         wbuf[0] = (buf[1] << 8) | buf[0];\r
1474     else if (cv->codepage == 1201) /* big endian */\r
1475         wbuf[0] = (buf[0] << 8) | buf[1];\r
1476     if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF)\r
1477         return_error(EILSEQ);\r
1478     if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)\r
1479     {\r
1480         if (bufsize < 4)\r
1481             return_error(EINVAL);\r
1482         if (cv->codepage == 1200) /* little endian */\r
1483             wbuf[1] = (buf[3] << 8) | buf[2];\r
1484         else if (cv->codepage == 1201) /* big endian */\r
1485             wbuf[1] = (buf[2] << 8) | buf[3];\r
1486         if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF))\r
1487             return_error(EILSEQ);\r
1488         *wbufsize = 2;\r
1489         return 4;\r
1490     }\r
1491     *wbufsize = 1;\r
1492     return 2;\r
1493 }\r
1494 \r
1495 static int\r
1496 utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
1497 {\r
1498     if (bufsize < 2)\r
1499         return_error(E2BIG);\r
1500     if (cv->codepage == 1200) /* little endian */\r
1501     {\r
1502         buf[0] = (wbuf[0] & 0x00FF);\r
1503         buf[1] = (wbuf[0] & 0xFF00) >> 8;\r
1504     }\r
1505     else if (cv->codepage == 1201) /* big endian */\r
1506     {\r
1507         buf[0] = (wbuf[0] & 0xFF00) >> 8;\r
1508         buf[1] = (wbuf[0] & 0x00FF);\r
1509     }\r
1510     if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)\r
1511     {\r
1512         if (bufsize < 4)\r
1513             return_error(E2BIG);\r
1514         if (cv->codepage == 1200) /* little endian */\r
1515         {\r
1516             buf[2] = (wbuf[1] & 0x00FF);\r
1517             buf[3] = (wbuf[1] & 0xFF00) >> 8;\r
1518         }\r
1519         else if (cv->codepage == 1201) /* big endian */\r
1520         {\r
1521             buf[2] = (wbuf[1] & 0xFF00) >> 8;\r
1522             buf[3] = (wbuf[1] & 0x00FF);\r
1523         }\r
1524         return 4;\r
1525     }\r
1526     return 2;\r
1527 }\r
1528 \r
1529 static int\r
1530 utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
1531 {\r
1532     uint wc;\r
1533 \r
1534     if (bufsize < 4)\r
1535         return_error(EINVAL);\r
1536     if (cv->codepage == 12000) /* little endian */\r
1537         wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];\r
1538     else if (cv->codepage == 12001) /* big endian */\r
1539         wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];\r
1540     if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc)\r
1541         return_error(EILSEQ);\r
1542     ucs4_to_utf16(wc, wbuf, wbufsize);\r
1543     return 4;\r
1544 }\r
1545 \r
1546 static int\r
1547 utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
1548 {\r
1549     uint wc;\r
1550 \r
1551     if (bufsize < 4)\r
1552         return_error(E2BIG);\r
1553     wc = utf16_to_ucs4(wbuf);\r
1554     if (cv->codepage == 12000) /* little endian */\r
1555     {\r
1556         buf[0] = wc & 0x000000FF;\r
1557         buf[1] = (wc & 0x0000FF00) >> 8;\r
1558         buf[2] = (wc & 0x00FF0000) >> 16;\r
1559         buf[3] = (wc & 0xFF000000) >> 24;\r
1560     }\r
1561     else if (cv->codepage == 12001) /* big endian */\r
1562     {\r
1563         buf[0] = (wc & 0xFF000000) >> 24;\r
1564         buf[1] = (wc & 0x00FF0000) >> 16;\r
1565         buf[2] = (wc & 0x0000FF00) >> 8;\r
1566         buf[3] = wc & 0x000000FF;\r
1567     }\r
1568     return 4;\r
1569 }\r
1570 \r
1571 /*\r
1572  * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)\r
1573  * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow\r
1574  *        1 byte Kana)\r
1575  * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte\r
1576  *        Kana - SO/SI)\r
1577  *\r
1578  * MultiByteToWideChar() and WideCharToMultiByte() behave differently\r
1579  * depending on Windows version.  On XP, WideCharToMultiByte() doesn't\r
1580  * terminate result sequence with ascii escape.  But Vista does.\r
1581  * Use MLang instead.\r
1582  */\r
1583 \r
1584 #define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift))\r
1585 #define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF)\r
1586 #define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF)\r
1587 \r
1588 #define ISO2022_SI  0\r
1589 #define ISO2022_SO  1\r
1590 \r
1591 /* shift in */\r
1592 static const char iso2022_SI_seq[] = "\x0F";\r
1593 /* shift out */\r
1594 static const char iso2022_SO_seq[] = "\x0E";\r
1595 \r
1596 typedef struct iso2022_esc_t iso2022_esc_t;\r
1597 struct iso2022_esc_t {\r
1598     const char *esc;\r
1599     int esc_len;\r
1600     int len;\r
1601     int cs;\r
1602 };\r
1603 \r
1604 #define ISO2022JP_CS_ASCII            0\r
1605 #define ISO2022JP_CS_JISX0201_ROMAN   1\r
1606 #define ISO2022JP_CS_JISX0201_KANA    2\r
1607 #define ISO2022JP_CS_JISX0208_1978    3\r
1608 #define ISO2022JP_CS_JISX0208_1983    4\r
1609 #define ISO2022JP_CS_JISX0212         5\r
1610 \r
1611 static iso2022_esc_t iso2022jp_esc[] = {\r
1612     {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII},\r
1613     {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN},\r
1614     {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA},\r
1615     {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */\r
1616     {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983},\r
1617     {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212},\r
1618     {NULL, 0, 0, 0}\r
1619 };\r
1620 \r
1621 static int\r
1622 iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
1623 {\r
1624     iso2022_esc_t *iesc = iso2022jp_esc;\r
1625     char tmp[MB_CHAR_MAX];\r
1626     int insize;\r
1627     HRESULT hr;\r
1628     DWORD dummy = 0;\r
1629     int len;\r
1630     int esc_len;\r
1631     int cs;\r
1632     int shift;\r
1633     int i;\r
1634 \r
1635     if (buf[0] == 0x1B)\r
1636     {\r
1637         for (i = 0; iesc[i].esc != NULL; ++i)\r
1638         {\r
1639             esc_len = iesc[i].esc_len;\r
1640             if (bufsize < esc_len)\r
1641             {\r
1642                 if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0)\r
1643                     return_error(EINVAL);\r
1644             }\r
1645             else\r
1646             {\r
1647                 if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0)\r
1648                 {\r
1649                     cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI);\r
1650                     *wbufsize = 0;\r
1651                     return esc_len;\r
1652                 }\r
1653             }\r
1654         }\r
1655         /* not supported escape sequence */\r
1656         return_error(EILSEQ);\r
1657     }\r
1658     else if (buf[0] == iso2022_SO_seq[0])\r
1659     {\r
1660         cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO);\r
1661         *wbufsize = 0;\r
1662         return 1;\r
1663     }\r
1664     else if (buf[0] == iso2022_SI_seq[0])\r
1665     {\r
1666         cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI);\r
1667         *wbufsize = 0;\r
1668         return 1;\r
1669     }\r
1670 \r
1671     cs = ISO2022_MODE_CS(cv->mode);\r
1672     shift = ISO2022_MODE_SHIFT(cv->mode);\r
1673 \r
1674     /* reset the mode for informal sequence */\r
1675     if (buf[0] < 0x20)\r
1676     {\r
1677         cs = ISO2022JP_CS_ASCII;\r
1678         shift = ISO2022_SI;\r
1679     }\r
1680 \r
1681     len = iesc[cs].len;\r
1682     if (bufsize < len)\r
1683         return_error(EINVAL);\r
1684     for (i = 0; i < len; ++i)\r
1685         if (!(buf[i] < 0x80))\r
1686             return_error(EILSEQ);\r
1687     esc_len = iesc[cs].esc_len;\r
1688     memcpy(tmp, iesc[cs].esc, esc_len);\r
1689     if (shift == ISO2022_SO)\r
1690     {\r
1691         memcpy(tmp + esc_len, iso2022_SO_seq, 1);\r
1692         esc_len += 1;\r
1693     }\r
1694     memcpy(tmp + esc_len, buf, len);\r
1695 \r
1696     if ((cv->codepage == 50220 || cv->codepage == 50221\r
1697                 || cv->codepage == 50222) && shift == ISO2022_SO)\r
1698     {\r
1699         /* XXX: shift-out cannot be used for mbtowc (both kernel and\r
1700          * mlang) */\r
1701         esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len;\r
1702         memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len);\r
1703         memcpy(tmp + esc_len, buf, len);\r
1704     }\r
1705 \r
1706     insize = len + esc_len;\r
1707     hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage,\r
1708             (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize);\r
1709     if (hr != S_OK || insize != len + esc_len)\r
1710         return_error(EILSEQ);\r
1711 \r
1712     /* Check for conversion error.  Assuming defaultChar is 0x3F. */\r
1713     /* ascii should be converted from ascii */\r
1714     if (wbuf[0] == buf[0]\r
1715             && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))\r
1716         return_error(EILSEQ);\r
1717 \r
1718     /* reset the mode for informal sequence */\r
1719     if (cv->mode != ISO2022_MODE(cs, shift))\r
1720         cv->mode = ISO2022_MODE(cs, shift);\r
1721 \r
1722     return len;\r
1723 }\r
1724 \r
1725 static int\r
1726 iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
1727 {\r
1728     iso2022_esc_t *iesc = iso2022jp_esc;\r
1729     char tmp[MB_CHAR_MAX];\r
1730     int tmpsize = MB_CHAR_MAX;\r
1731     int insize = wbufsize;\r
1732     HRESULT hr;\r
1733     DWORD dummy = 0;\r
1734     int len;\r
1735     int esc_len;\r
1736     int cs;\r
1737     int shift;\r
1738     int i;\r
1739 \r
1740     /*\r
1741      * MultiByte = [escape sequence] + character + [escape sequence]\r
1742      *\r
1743      * Whether trailing escape sequence is added depends on which API is\r
1744      * used (kernel or MLang, and its version).\r
1745      */\r
1746     hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage,\r
1747             (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize);\r
1748     if (hr != S_OK || insize != wbufsize)\r
1749         return_error(EILSEQ);\r
1750     else if (bufsize < tmpsize)\r
1751         return_error(E2BIG);\r
1752 \r
1753     if (tmpsize == 1)\r
1754     {\r
1755         cs = ISO2022JP_CS_ASCII;\r
1756         esc_len = 0;\r
1757     }\r
1758     else\r
1759     {\r
1760         for (i = 1; iesc[i].esc != NULL; ++i)\r
1761         {\r
1762             esc_len = iesc[i].esc_len;\r
1763             if (strncmp(tmp, iesc[i].esc, esc_len) == 0)\r
1764             {\r
1765                 cs = iesc[i].cs;\r
1766                 break;\r
1767             }\r
1768         }\r
1769         if (iesc[i].esc == NULL)\r
1770             /* not supported escape sequence */\r
1771             return_error(EILSEQ);\r
1772     }\r
1773 \r
1774     shift = ISO2022_SI;\r
1775     if (tmp[esc_len] == iso2022_SO_seq[0])\r
1776     {\r
1777         shift = ISO2022_SO;\r
1778         esc_len += 1;\r
1779     }\r
1780 \r
1781     len = iesc[cs].len;\r
1782 \r
1783     /* Check for converting error.  Assuming defaultChar is 0x3F. */\r
1784     /* ascii should be converted from ascii */\r
1785     if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80))\r
1786         return_error(EILSEQ);\r
1787     else if (tmpsize < esc_len + len)\r
1788         return_error(EILSEQ);\r
1789 \r
1790     if (cv->mode == ISO2022_MODE(cs, shift))\r
1791     {\r
1792         /* remove escape sequence */\r
1793         if (esc_len != 0)\r
1794             memmove(tmp, tmp + esc_len, len);\r
1795         esc_len = 0;\r
1796     }\r
1797     else\r
1798     {\r
1799         if (cs == ISO2022JP_CS_ASCII)\r
1800         {\r
1801             esc_len = iesc[ISO2022JP_CS_ASCII].esc_len;\r
1802             memmove(tmp + esc_len, tmp, len);\r
1803             memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len);\r
1804         }\r
1805         if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO)\r
1806         {\r
1807             /* shift-in before changing to other mode */\r
1808             memmove(tmp + 1, tmp, len + esc_len);\r
1809             memcpy(tmp, iso2022_SI_seq, 1);\r
1810             esc_len += 1;\r
1811         }\r
1812     }\r
1813 \r
1814     if (bufsize < len + esc_len)\r
1815         return_error(E2BIG);\r
1816     memcpy(buf, tmp, len + esc_len);\r
1817     cv->mode = ISO2022_MODE(cs, shift);\r
1818     return len + esc_len;\r
1819 }\r
1820 \r
1821 static int\r
1822 iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize)\r
1823 {\r
1824     iso2022_esc_t *iesc = iso2022jp_esc;\r
1825     int esc_len;\r
1826 \r
1827     if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))\r
1828     {\r
1829         esc_len = 0;\r
1830         if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)\r
1831             esc_len += 1;\r
1832         if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)\r
1833             esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;\r
1834         if (bufsize < esc_len)\r
1835             return_error(E2BIG);\r
1836 \r
1837         esc_len = 0;\r
1838         if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)\r
1839         {\r
1840             memcpy(buf, iso2022_SI_seq, 1);\r
1841             esc_len += 1;\r
1842         }\r
1843         if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)\r
1844         {\r
1845             memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc,\r
1846                     iesc[ISO2022JP_CS_ASCII].esc_len);\r
1847             esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;\r
1848         }\r
1849         return esc_len;\r
1850     }\r
1851     return 0;\r
1852 }\r
1853 \r
1854 #if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL)\r
1855 BOOL WINAPI\r
1856 DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)\r
1857 {\r
1858     switch( fdwReason )\r
1859     {\r
1860     case DLL_PROCESS_ATTACH:\r
1861         hwiniconv = (HMODULE)hinstDLL;\r
1862         break;\r
1863     case DLL_THREAD_ATTACH:\r
1864     case DLL_THREAD_DETACH:\r
1865     case DLL_PROCESS_DETACH:\r
1866         break;\r
1867     }\r
1868     return TRUE;\r
1869 }\r
1870 #endif\r
1871 \r
1872 #if defined(MAKE_EXE)\r
1873 #include <stdio.h>\r
1874 #include <fcntl.h>\r
1875 #include <io.h>\r
1876 int\r
1877 main(int argc, char **argv)\r
1878 {\r
1879     char *fromcode = NULL;\r
1880     char *tocode = NULL;\r
1881     int i;\r
1882     char inbuf[BUFSIZ];\r
1883     char outbuf[BUFSIZ];\r
1884     const char *pin;\r
1885     char *pout;\r
1886     size_t inbytesleft;\r
1887     size_t outbytesleft;\r
1888     size_t rest = 0;\r
1889     iconv_t cd;\r
1890     size_t r;\r
1891     FILE *in = stdin;\r
1892 \r
1893     _setmode(_fileno(stdin), _O_BINARY);\r
1894     _setmode(_fileno(stdout), _O_BINARY);\r
1895 \r
1896     for (i = 1; i < argc; ++i)\r
1897     {\r
1898         if (strcmp(argv[i], "-l") == 0)\r
1899         {\r
1900             for (i = 0; codepage_alias[i].name != NULL; ++i)\r
1901                 printf("%s\n", codepage_alias[i].name);\r
1902             return 0;\r
1903         }\r
1904 \r
1905         if (strcmp(argv[i], "-f") == 0)\r
1906             fromcode = argv[++i];\r
1907         else if (strcmp(argv[i], "-t") == 0)\r
1908             tocode = argv[++i];\r
1909         else\r
1910         {\r
1911             in = fopen(argv[i], "rb");\r
1912             if (in == NULL)\r
1913             {\r
1914                 fprintf(stderr, "cannot open %s\n", argv[i]);\r
1915                 return 1;\r
1916             }\r
1917             break;\r
1918         }\r
1919     }\r
1920 \r
1921     if (fromcode == NULL || tocode == NULL)\r
1922     {\r
1923         printf("usage: %s -f from-enc -t to-enc [file]\n", argv[0]);\r
1924         return 0;\r
1925     }\r
1926 \r
1927     cd = iconv_open(tocode, fromcode);\r
1928     if (cd == (iconv_t)(-1))\r
1929     {\r
1930         perror("iconv_open error");\r
1931         return 1;\r
1932     }\r
1933 \r
1934     while ((inbytesleft = fread(inbuf + rest, 1, sizeof(inbuf) - rest, in)) != 0\r
1935             || rest != 0)\r
1936     {\r
1937         inbytesleft += rest;\r
1938         pin = inbuf;\r
1939         pout = outbuf;\r
1940         outbytesleft = sizeof(outbuf);\r
1941         r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft);\r
1942         fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout);\r
1943         if (r == (size_t)(-1) && errno != EINVAL && errno != E2BIG)\r
1944         {\r
1945             perror("conversion error");\r
1946             return 1;\r
1947         }\r
1948         memmove(inbuf, pin, inbytesleft);\r
1949         rest = inbytesleft;\r
1950     }\r
1951     pout = outbuf;\r
1952     outbytesleft = sizeof(outbuf);\r
1953     r = iconv(cd, NULL, NULL, &pout, &outbytesleft);\r
1954     fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout);\r
1955     if (r == (size_t)(-1))\r
1956     {\r
1957         perror("conversion error");\r
1958         return 1;\r
1959     }\r
1960 \r
1961     iconv_close(cd);\r
1962 \r
1963     return 0;\r
1964 }\r
1965 #endif\r