/* | |
* iconv library implemented with Win32 API. | |
* | |
* This file is placed in the public domain. | |
* | |
* Maintainer: Yukihiro Nakadaira <yukihiro.nakadaira@gmail.com> | |
* | |
* If $WINICONV_LIBICONV_DLL environment variable was defined, win_iconv | |
* loads the specified DLL dynamically and uses it. If loading the DLL | |
* or iconv_open() failed, falls back to internal conversion. | |
* $WINICONV_LIBICONV_DLL is a comma separated list. The first loadable | |
* DLL is used. The specified DLL should have iconv_open(), | |
* iconv_close() and iconv() functions. Or these functions can be | |
* libiconv_open(), libiconv_close() and libiconv(). | |
* | |
* Win32 API does not support strict encoding conversion for some | |
* codepage. And MLang function drop or replace invalid bytes and does | |
* not return useful error status as iconv. This implementation cannot | |
* be used for encoding validation purpose. | |
*/ | |
/* for WC_NO_BEST_FIT_CHARS */ | |
#ifndef WINVER | |
# define WINVER 0x0500 | |
#endif | |
#define STRICT | |
#include <windows.h> | |
#include <errno.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#if 0 | |
# define MAKE_EXE | |
# define MAKE_DLL | |
# define USE_LIBICONV_DLL | |
#endif | |
#if !defined(DEFAULT_LIBICONV_DLL) | |
# define DEFAULT_LIBICONV_DLL "" | |
#endif | |
#define MB_CHAR_MAX 16 | |
#define UNICODE_MODE_BOM_DONE 1 | |
#define UNICODE_MODE_SWAPPED 2 | |
#define FLAG_USE_BOM_ENDIAN 1 | |
#define FLAG_TRANSLIT 2 /* //TRANSLIT */ | |
#define FLAG_IGNORE 4 /* //IGNORE (not implemented) */ | |
#define return_error(code) \ | |
do { \ | |
errno = code; \ | |
return -1; \ | |
} while (0) | |
#define xstrlcpy(dst, src, size) \ | |
do { \ | |
strncpy(dst, src, size); \ | |
dst[size - 1] = 0; \ | |
} while (0) | |
#define xstrlcpyn(dst, src, srclen, size) \ | |
xstrlcpy(dst, src, xmin((srclen) + 1, size)) | |
#define xmin(a, b) ((a) < (b) ? (a) : (b)) | |
#define xmax(a, b) ((a) > (b) ? (a) : (b)) | |
#define STATIC_STRLEN(arr) (sizeof(arr) - 1) | |
typedef unsigned char uchar; | |
typedef unsigned short ushort; | |
typedef unsigned int uint; | |
typedef void* iconv_t; | |
iconv_t iconv_open(const char *tocode, const char *fromcode); | |
int iconv_close(iconv_t cd); | |
size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); | |
/* libiconv interface for vim */ | |
#if defined(MAKE_DLL) | |
int | |
iconvctl (iconv_t cd, int request, void* argument) | |
{ | |
/* not supported */ | |
return 0; | |
} | |
#endif | |
typedef struct compat_t compat_t; | |
typedef struct csconv_t csconv_t; | |
typedef struct rec_iconv_t rec_iconv_t; | |
typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode); | |
typedef int (*f_iconv_close)(iconv_t cd); | |
typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); | |
typedef int* (*f_errno)(void); | |
typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | |
typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | |
typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize); | |
typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize); | |
#define COMPAT_IN 1 | |
#define COMPAT_OUT 2 | |
/* unicode mapping for compatibility with other conversion table. */ | |
struct compat_t { | |
uint in; | |
uint out; | |
uint flag; | |
}; | |
struct csconv_t { | |
int codepage; | |
int flags; | |
f_mbtowc mbtowc; | |
f_wctomb wctomb; | |
f_mblen mblen; | |
f_flush flush; | |
DWORD mode; | |
compat_t *compat; | |
}; | |
struct rec_iconv_t { | |
iconv_t cd; | |
f_iconv_close iconv_close; | |
f_iconv iconv; | |
f_errno _errno; | |
csconv_t from; | |
csconv_t to; | |
#if defined(USE_LIBICONV_DLL) | |
HMODULE hlibiconv; | |
#endif | |
}; | |
static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); | |
static int win_iconv_close(iconv_t cd); | |
static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); | |
static int load_mlang(); | |
static csconv_t make_csconv(const char *name); | |
static int name_to_codepage(const char *name); | |
static uint utf16_to_ucs4(const ushort *wbuf); | |
static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize); | |
static int is_unicode(int codepage); | |
static int mbtowc_flags(int codepage); | |
static int must_use_null_useddefaultchar(int codepage); | |
static void check_utf_bom(rec_iconv_t *cd, ushort *wbuf, int *wbufsize); | |
static char *strrstr(const char *str, const char *token); | |
#if defined(USE_LIBICONV_DLL) | |
static int libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); | |
static PVOID MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size); | |
static HMODULE find_imported_module_by_funcname(HMODULE hModule, const char *funcname); | |
static HMODULE hwiniconv; | |
static HMODULE hlastdll; /* keep dll loaded for efficiency (unnecessary?) */ | |
#endif | |
static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); | |
static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); | |
static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); | |
static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize); | |
static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize); | |
static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | |
static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | |
static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | |
static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | |
static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | |
static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | |
static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | |
static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | |
static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | |
static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | |
static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize); | |
static struct { | |
int codepage; | |
const char *name; | |
} codepage_alias[] = { | |
{65001, "CP65001"}, | |
{65001, "UTF8"}, | |
{65001, "UTF-8"}, | |
{1200, "CP1200"}, | |
{1200, "UTF16LE"}, | |
{1200, "UTF-16LE"}, | |
{1200, "UCS2LE"}, | |
{1200, "UCS-2LE"}, | |
{1201, "CP1201"}, | |
{1201, "UTF16BE"}, | |
{1201, "UTF-16BE"}, | |
{1201, "UCS2BE"}, | |
{1201, "UCS-2BE"}, | |
{1201, "unicodeFFFE"}, | |
{12000, "CP12000"}, | |
{12000, "UTF32LE"}, | |
{12000, "UTF-32LE"}, | |
{12000, "UCS4LE"}, | |
{12000, "UCS-4LE"}, | |
{12001, "CP12001"}, | |
{12001, "UTF32BE"}, | |
{12001, "UTF-32BE"}, | |
{12001, "UCS4BE"}, | |
{12001, "UCS-4BE"}, | |
#ifndef GLIB_COMPILATION | |
/* | |
* Default is big endian. | |
* See rfc2781 4.3 Interpreting text labelled as UTF-16. | |
*/ | |
{1201, "UTF16"}, | |
{1201, "UTF-16"}, | |
{12001, "UTF32"}, | |
{12001, "UTF-32"}, | |
{12001, "UCS-4"}, | |
{12001, "UCS4"}, | |
#else | |
/* Default is little endian, because the platform is */ | |
{1200, "UTF16"}, | |
{1200, "UTF-16"}, | |
{1200, "UCS2"}, | |
{1200, "UCS-2"}, | |
{12000, "UTF32"}, | |
{12000, "UTF-32"}, | |
{12000, "UCS4"}, | |
{12000, "UCS-4"}, | |
#endif | |
/* copy from libiconv `iconv -l` */ | |
/* !IsValidCodePage(367) */ | |
{20127, "ANSI_X3.4-1968"}, | |
{20127, "ANSI_X3.4-1986"}, | |
{20127, "ASCII"}, | |
{20127, "CP367"}, | |
{20127, "IBM367"}, | |
{20127, "ISO-IR-6"}, | |
{20127, "ISO646-US"}, | |
{20127, "ISO_646.IRV:1991"}, | |
{20127, "US"}, | |
{20127, "US-ASCII"}, | |
{20127, "CSASCII"}, | |
/* !IsValidCodePage(819) */ | |
{1252, "CP819"}, | |
{1252, "IBM819"}, | |
{28591, "ISO-8859-1"}, | |
{28591, "ISO-IR-100"}, | |
{28591, "ISO8859-1"}, | |
{28591, "ISO_8859-1"}, | |
{28591, "ISO_8859-1:1987"}, | |
{28591, "L1"}, | |
{28591, "LATIN1"}, | |
{28591, "CSISOLATIN1"}, | |
{1250, "CP1250"}, | |
{1250, "MS-EE"}, | |
{1250, "WINDOWS-1250"}, | |
{1251, "CP1251"}, | |
{1251, "MS-CYRL"}, | |
{1251, "WINDOWS-1251"}, | |
{1252, "CP1252"}, | |
{1252, "MS-ANSI"}, | |
{1252, "WINDOWS-1252"}, | |
{1253, "CP1253"}, | |
{1253, "MS-GREEK"}, | |
{1253, "WINDOWS-1253"}, | |
{1254, "CP1254"}, | |
{1254, "MS-TURK"}, | |
{1254, "WINDOWS-1254"}, | |
{1255, "CP1255"}, | |
{1255, "MS-HEBR"}, | |
{1255, "WINDOWS-1255"}, | |
{1256, "CP1256"}, | |
{1256, "MS-ARAB"}, | |
{1256, "WINDOWS-1256"}, | |
{1257, "CP1257"}, | |
{1257, "WINBALTRIM"}, | |
{1257, "WINDOWS-1257"}, | |
{1258, "CP1258"}, | |
{1258, "WINDOWS-1258"}, | |
{850, "850"}, | |
{850, "CP850"}, | |
{850, "IBM850"}, | |
{850, "CSPC850MULTILINGUAL"}, | |
/* !IsValidCodePage(862) */ | |
{862, "862"}, | |
{862, "CP862"}, | |
{862, "IBM862"}, | |
{862, "CSPC862LATINHEBREW"}, | |
{866, "866"}, | |
{866, "CP866"}, | |
{866, "IBM866"}, | |
{866, "CSIBM866"}, | |
/* !IsValidCodePage(154) */ | |
{154, "CP154"}, | |
{154, "CYRILLIC-ASIAN"}, | |
{154, "PT154"}, | |
{154, "PTCP154"}, | |
{154, "CSPTCP154"}, | |
/* !IsValidCodePage(1133) */ | |
{1133, "CP1133"}, | |
{1133, "IBM-CP1133"}, | |
{874, "CP874"}, | |
{874, "WINDOWS-874"}, | |
/* !IsValidCodePage(51932) */ | |
{51932, "CP51932"}, | |
{51932, "MS51932"}, | |
{51932, "WINDOWS-51932"}, | |
{51932, "EUC-JP"}, | |
{932, "CP932"}, | |
{932, "MS932"}, | |
{932, "SHIFFT_JIS"}, | |
{932, "SHIFFT_JIS-MS"}, | |
{932, "SJIS"}, | |
{932, "SJIS-MS"}, | |
{932, "SJIS-OPEN"}, | |
{932, "SJIS-WIN"}, | |
{932, "WINDOWS-31J"}, | |
{932, "WINDOWS-932"}, | |
{932, "CSWINDOWS31J"}, | |
{50221, "CP50221"}, | |
{50221, "ISO-2022-JP"}, | |
{50221, "ISO-2022-JP-MS"}, | |
{50221, "ISO2022-JP"}, | |
{50221, "ISO2022-JP-MS"}, | |
{50221, "MS50221"}, | |
{50221, "WINDOWS-50221"}, | |
{936, "CP936"}, | |
{936, "GBK"}, | |
{936, "MS936"}, | |
{936, "WINDOWS-936"}, | |
{950, "CP950"}, | |
{950, "BIG5"}, | |
{949, "CP949"}, | |
{949, "UHC"}, | |
{949, "EUC-KR"}, | |
{1361, "CP1361"}, | |
{1361, "JOHAB"}, | |
{437, "437"}, | |
{437, "CP437"}, | |
{437, "IBM437"}, | |
{437, "CSPC8CODEPAGE437"}, | |
{737, "CP737"}, | |
{775, "CP775"}, | |
{775, "IBM775"}, | |
{775, "CSPC775BALTIC"}, | |
{852, "852"}, | |
{852, "CP852"}, | |
{852, "IBM852"}, | |
{852, "CSPCP852"}, | |
/* !IsValidCodePage(853) */ | |
{853, "CP853"}, | |
{855, "855"}, | |
{855, "CP855"}, | |
{855, "IBM855"}, | |
{855, "CSIBM855"}, | |
{857, "857"}, | |
{857, "CP857"}, | |
{857, "IBM857"}, | |
{857, "CSIBM857"}, | |
/* !IsValidCodePage(858) */ | |
{858, "CP858"}, | |
{860, "860"}, | |
{860, "CP860"}, | |
{860, "IBM860"}, | |
{860, "CSIBM860"}, | |
{861, "861"}, | |
{861, "CP-IS"}, | |
{861, "CP861"}, | |
{861, "IBM861"}, | |
{861, "CSIBM861"}, | |
{863, "863"}, | |
{863, "CP863"}, | |
{863, "IBM863"}, | |
{863, "CSIBM863"}, | |
{864, "CP864"}, | |
{864, "IBM864"}, | |
{864, "CSIBM864"}, | |
{865, "865"}, | |
{865, "CP865"}, | |
{865, "IBM865"}, | |
{865, "CSIBM865"}, | |
{869, "869"}, | |
{869, "CP-GR"}, | |
{869, "CP869"}, | |
{869, "IBM869"}, | |
{869, "CSIBM869"}, | |
/* !IsValidCodePage(1152) */ | |
{1125, "CP1125"}, | |
/* | |
* Code Page Identifiers | |
* http://msdn2.microsoft.com/en-us/library/ms776446.aspx | |
*/ | |
{37, "IBM037"}, /* IBM EBCDIC US-Canada */ | |
{437, "IBM437"}, /* OEM United States */ | |
{500, "IBM500"}, /* IBM EBCDIC International */ | |
{708, "ASMO-708"}, /* Arabic (ASMO 708) */ | |
/* 709 Arabic (ASMO-449+, BCON V4) */ | |
/* 710 Arabic - Transparent Arabic */ | |
{720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */ | |
{737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */ | |
{775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */ | |
{850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */ | |
{852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */ | |
{855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */ | |
{857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */ | |
{858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */ | |
{860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */ | |
{861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */ | |
{862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */ | |
{863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */ | |
{864, "IBM864"}, /* OEM Arabic; Arabic (864) */ | |
{865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */ | |
{866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */ | |
{869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */ | |
{870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */ | |
{874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */ | |
{875, "cp875"}, /* IBM EBCDIC Greek Modern */ | |
{932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */ | |
{932, "shift-jis"}, /* alternative name for it */ | |
{936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */ | |
{949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */ | |
{950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */ | |
{1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */ | |
{1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */ | |
{1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */ | |
{1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */ | |
{1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */ | |
{1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */ | |
{1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */ | |
{1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */ | |
{1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */ | |
{1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */ | |
{1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */ | |
{1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */ | |
{1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */ | |
{1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */ | |
{1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */ | |
{1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */ | |
{1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */ | |
{1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */ | |
{1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */ | |
{1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */ | |
{1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */ | |
{1361, "Johab"}, /* Korean (Johab) */ | |
{10000, "macintosh"}, /* MAC Roman; Western European (Mac) */ | |
{10001, "x-mac-japanese"}, /* Japanese (Mac) */ | |
{10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */ | |
{10003, "x-mac-korean"}, /* Korean (Mac) */ | |
{10004, "x-mac-arabic"}, /* Arabic (Mac) */ | |
{10005, "x-mac-hebrew"}, /* Hebrew (Mac) */ | |
{10006, "x-mac-greek"}, /* Greek (Mac) */ | |
{10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */ | |
{10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */ | |
{10010, "x-mac-romanian"}, /* Romanian (Mac) */ | |
{10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */ | |
{10021, "x-mac-thai"}, /* Thai (Mac) */ | |
{10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */ | |
{10079, "x-mac-icelandic"}, /* Icelandic (Mac) */ | |
{10081, "x-mac-turkish"}, /* Turkish (Mac) */ | |
{10082, "x-mac-croatian"}, /* Croatian (Mac) */ | |
{20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */ | |
{20001, "x-cp20001"}, /* TCA Taiwan */ | |
{20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */ | |
{20003, "x-cp20003"}, /* IBM5550 Taiwan */ | |
{20004, "x-cp20004"}, /* TeleText Taiwan */ | |
{20005, "x-cp20005"}, /* Wang Taiwan */ | |
{20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */ | |
{20106, "x-IA5-German"}, /* IA5 German (7-bit) */ | |
{20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */ | |
{20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */ | |
{20127, "us-ascii"}, /* US-ASCII (7-bit) */ | |
{20261, "x-cp20261"}, /* T.61 */ | |
{20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */ | |
{20273, "IBM273"}, /* IBM EBCDIC Germany */ | |
{20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */ | |
{20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */ | |
{20280, "IBM280"}, /* IBM EBCDIC Italy */ | |
{20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */ | |
{20285, "IBM285"}, /* IBM EBCDIC United Kingdom */ | |
{20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */ | |
{20297, "IBM297"}, /* IBM EBCDIC France */ | |
{20420, "IBM420"}, /* IBM EBCDIC Arabic */ | |
{20423, "IBM423"}, /* IBM EBCDIC Greek */ | |
{20424, "IBM424"}, /* IBM EBCDIC Hebrew */ | |
{20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */ | |
{20838, "IBM-Thai"}, /* IBM EBCDIC Thai */ | |
{20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */ | |
{20871, "IBM871"}, /* IBM EBCDIC Icelandic */ | |
{20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */ | |
{20905, "IBM905"}, /* IBM EBCDIC Turkish */ | |
{20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */ | |
{20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */ | |
{20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */ | |
{20949, "x-cp20949"}, /* Korean Wansung */ | |
{21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */ | |
/* 21027 (deprecated) */ | |
{21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */ | |
{28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ | |
{28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ | |
{28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ | |
{28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ | |
{28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */ | |
{28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */ | |
{28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */ | |
{28594, "iso8859-4"}, /* ISO 8859-4 Baltic */ | |
{28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */ | |
{28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */ | |
{28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */ | |
{28596, "iso8859-6"}, /* ISO 8859-6 Arabic */ | |
{28597, "iso-8859-7"}, /* ISO 8859-7 Greek */ | |
{28597, "iso8859-7"}, /* ISO 8859-7 Greek */ | |
{28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ | |
{28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ | |
{28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */ | |
{28599, "iso8859-9"}, /* ISO 8859-9 Turkish */ | |
{28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */ | |
{28603, "iso8859-13"}, /* ISO 8859-13 Estonian */ | |
{28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */ | |
{28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */ | |
{29001, "x-Europa"}, /* Europa 3 */ | |
{38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ | |
{38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ | |
{50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */ | |
{50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */ | |
{50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */ | |
{50225, "iso-2022-kr"}, /* ISO 2022 Korean */ | |
{50225, "iso2022-kr"}, /* ISO 2022 Korean */ | |
{50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */ | |
/* 50229 ISO 2022 Traditional Chinese */ | |
/* 50930 EBCDIC Japanese (Katakana) Extended */ | |
/* 50931 EBCDIC US-Canada and Japanese */ | |
/* 50933 EBCDIC Korean Extended and Korean */ | |
/* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */ | |
/* 50936 EBCDIC Simplified Chinese */ | |
/* 50937 EBCDIC US-Canada and Traditional Chinese */ | |
/* 50939 EBCDIC Japanese (Latin) Extended and Japanese */ | |
{51932, "euc-jp"}, /* EUC Japanese */ | |
{51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */ | |
{51949, "euc-kr"}, /* EUC Korean */ | |
/* 51950 EUC Traditional Chinese */ | |
{52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */ | |
{54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */ | |
{57002, "x-iscii-de"}, /* ISCII Devanagari */ | |
{57003, "x-iscii-be"}, /* ISCII Bengali */ | |
{57004, "x-iscii-ta"}, /* ISCII Tamil */ | |
{57005, "x-iscii-te"}, /* ISCII Telugu */ | |
{57006, "x-iscii-as"}, /* ISCII Assamese */ | |
{57007, "x-iscii-or"}, /* ISCII Oriya */ | |
{57008, "x-iscii-ka"}, /* ISCII Kannada */ | |
{57009, "x-iscii-ma"}, /* ISCII Malayalam */ | |
{57010, "x-iscii-gu"}, /* ISCII Gujarati */ | |
{57011, "x-iscii-pa"}, /* ISCII Punjabi */ | |
{0, NULL} | |
}; | |
/* | |
* SJIS SHIFTJIS table CP932 table | |
* ---- --------------------------- -------------------------------- | |
* 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS | |
* 7E U+203E OVERLINE U+007E TILDE | |
* 815C U+2014 EM DASH U+2015 HORIZONTAL BAR | |
* 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS | |
* 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE | |
* 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO | |
* 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS | |
* 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN | |
* 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN | |
* 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN | |
* | |
* EUC-JP and ISO-2022-JP should be compatible with CP932. | |
* | |
* Kernel and MLang have different Unicode mapping table. Make sure | |
* which API is used. | |
*/ | |
static compat_t cp932_compat[] = { | |
{0x00A5, 0x005C, COMPAT_OUT}, | |
{0x203E, 0x007E, COMPAT_OUT}, | |
{0x2014, 0x2015, COMPAT_OUT}, | |
{0x301C, 0xFF5E, COMPAT_OUT}, | |
{0x2016, 0x2225, COMPAT_OUT}, | |
{0x2212, 0xFF0D, COMPAT_OUT}, | |
{0x00A2, 0xFFE0, COMPAT_OUT}, | |
{0x00A3, 0xFFE1, COMPAT_OUT}, | |
{0x00AC, 0xFFE2, COMPAT_OUT}, | |
{0, 0, 0} | |
}; | |
static compat_t cp20932_compat[] = { | |
{0x00A5, 0x005C, COMPAT_OUT}, | |
{0x203E, 0x007E, COMPAT_OUT}, | |
{0x2014, 0x2015, COMPAT_OUT}, | |
{0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN}, | |
{0x2225, 0x2016, COMPAT_OUT|COMPAT_IN}, | |
{0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN}, | |
{0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN}, | |
{0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN}, | |
{0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN}, | |
{0, 0, 0} | |
}; | |
static compat_t *cp51932_compat = cp932_compat; | |
/* cp20932_compat for kernel. cp932_compat for mlang. */ | |
static compat_t *cp5022x_compat = cp932_compat; | |
typedef HRESULT (WINAPI *CONVERTINETSTRING)( | |
LPDWORD lpdwMode, | |
DWORD dwSrcEncoding, | |
DWORD dwDstEncoding, | |
LPCSTR lpSrcStr, | |
LPINT lpnSrcSize, | |
LPBYTE lpDstStr, | |
LPINT lpnDstSize | |
); | |
typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)( | |
LPDWORD lpdwMode, | |
DWORD dwSrcEncoding, | |
LPCSTR lpSrcStr, | |
LPINT lpnMultiCharCount, | |
LPWSTR lpDstStr, | |
LPINT lpnWideCharCount | |
); | |
typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)( | |
LPDWORD lpdwMode, | |
DWORD dwEncoding, | |
LPCWSTR lpSrcStr, | |
LPINT lpnWideCharCount, | |
LPSTR lpDstStr, | |
LPINT lpnMultiCharCount | |
); | |
typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)( | |
DWORD dwSrcEncoding, | |
DWORD dwDstEncoding | |
); | |
typedef HRESULT (WINAPI *LCIDTORFC1766A)( | |
LCID Locale, | |
LPSTR pszRfc1766, | |
int nChar | |
); | |
typedef HRESULT (WINAPI *LCIDTORFC1766W)( | |
LCID Locale, | |
LPWSTR pszRfc1766, | |
int nChar | |
); | |
typedef HRESULT (WINAPI *RFC1766TOLCIDA)( | |
LCID *pLocale, | |
LPSTR pszRfc1766 | |
); | |
typedef HRESULT (WINAPI *RFC1766TOLCIDW)( | |
LCID *pLocale, | |
LPWSTR pszRfc1766 | |
); | |
static CONVERTINETSTRING ConvertINetString; | |
static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode; | |
static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte; | |
static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable; | |
static LCIDTORFC1766A LcidToRfc1766A; | |
static RFC1766TOLCIDA Rfc1766ToLcidA; | |
static int | |
load_mlang() | |
{ | |
HMODULE h; | |
if (ConvertINetString != NULL) | |
return TRUE; | |
h = LoadLibrary("mlang.dll"); | |
if (!h) | |
return FALSE; | |
ConvertINetString = (CONVERTINETSTRING)GetProcAddress(h, "ConvertINetString"); | |
ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddress(h, "ConvertINetMultiByteToUnicode"); | |
ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddress(h, "ConvertINetUnicodeToMultiByte"); | |
IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddress(h, "IsConvertINetStringAvailable"); | |
LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddress(h, "LcidToRfc1766A"); | |
Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddress(h, "Rfc1766ToLcidA"); | |
return TRUE; | |
} | |
iconv_t | |
iconv_open(const char *tocode, const char *fromcode) | |
{ | |
rec_iconv_t *cd; | |
cd = (rec_iconv_t *)calloc(1, sizeof(rec_iconv_t)); | |
if (cd == NULL) | |
{ | |
errno = ENOMEM; | |
return (iconv_t)(-1); | |
} | |
#if defined(USE_LIBICONV_DLL) | |
if (libiconv_iconv_open(cd, tocode, fromcode)) | |
return (iconv_t)cd; | |
#endif | |
if (win_iconv_open(cd, tocode, fromcode)) | |
return (iconv_t)cd; | |
free(cd); | |
errno = EINVAL; | |
return (iconv_t)(-1); | |
} | |
int | |
iconv_close(iconv_t _cd) | |
{ | |
rec_iconv_t *cd = (rec_iconv_t *)_cd; | |
int r = cd->iconv_close(cd->cd); | |
int e = *(cd->_errno()); | |
#if defined(USE_LIBICONV_DLL) | |
if (cd->hlibiconv != NULL) | |
FreeLibrary(cd->hlibiconv); | |
#endif | |
free(cd); | |
errno = e; | |
return r; | |
} | |
size_t | |
iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) | |
{ | |
rec_iconv_t *cd = (rec_iconv_t *)_cd; | |
size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft); | |
errno = *(cd->_errno()); | |
return r; | |
} | |
static int | |
win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode) | |
{ | |
cd->from = make_csconv(fromcode); | |
cd->to = make_csconv(tocode); | |
if (cd->from.codepage == -1 || cd->to.codepage == -1) | |
return FALSE; | |
cd->iconv_close = win_iconv_close; | |
cd->iconv = win_iconv; | |
cd->_errno = _errno; | |
cd->cd = (iconv_t)cd; | |
return TRUE; | |
} | |
static int | |
win_iconv_close(iconv_t cd) | |
{ | |
return 0; | |
} | |
static size_t | |
win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) | |
{ | |
rec_iconv_t *cd = (rec_iconv_t *)_cd; | |
ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */ | |
int insize; | |
int outsize; | |
int wsize; | |
DWORD mode; | |
uint wc; | |
compat_t *cp; | |
int i; | |
if (inbuf == NULL || *inbuf == NULL) | |
{ | |
if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL) | |
{ | |
outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft); | |
if (outsize == -1) | |
return (size_t)(-1); | |
*outbuf += outsize; | |
*outbytesleft -= outsize; | |
} | |
if (is_unicode(cd->from.codepage) && (cd->from.mode & UNICODE_MODE_SWAPPED)) | |
cd->from.codepage ^= 1; | |
cd->from.mode = 0; | |
cd->to.mode = 0; | |
return 0; | |
} | |
while (*inbytesleft != 0) | |
{ | |
mode = cd->from.mode; | |
wsize = MB_CHAR_MAX; | |
insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize); | |
if (insize == -1) | |
return (size_t)(-1); | |
if (is_unicode(cd->from.codepage) && !(cd->from.mode & UNICODE_MODE_BOM_DONE)) | |
{ | |
check_utf_bom(cd, wbuf, &wsize); | |
cd->from.mode |= UNICODE_MODE_BOM_DONE; | |
} | |
if (wsize == 0) | |
{ | |
*inbuf += insize; | |
*inbytesleft -= insize; | |
continue; | |
} | |
if (cd->from.compat != NULL) | |
{ | |
wc = utf16_to_ucs4(wbuf); | |
cp = cd->from.compat; | |
for (i = 0; cp[i].in != 0; ++i) | |
{ | |
if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc) | |
{ | |
ucs4_to_utf16(cp[i].in, wbuf, &wsize); | |
break; | |
} | |
} | |
} | |
if (cd->to.compat != NULL) | |
{ | |
wc = utf16_to_ucs4(wbuf); | |
cp = cd->to.compat; | |
for (i = 0; cp[i].in != 0; ++i) | |
{ | |
if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc) | |
{ | |
ucs4_to_utf16(cp[i].out, wbuf, &wsize); | |
break; | |
} | |
} | |
} | |
outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft); | |
if (outsize == -1) | |
{ | |
cd->from.mode = mode; | |
return (size_t)(-1); | |
} | |
*inbuf += insize; | |
*outbuf += outsize; | |
*inbytesleft -= insize; | |
*outbytesleft -= outsize; | |
} | |
return 0; | |
} | |
static csconv_t | |
make_csconv(const char *_name) | |
{ | |
CPINFOEX cpinfoex; | |
csconv_t cv; | |
int use_compat = TRUE; | |
int flag = 0; | |
char name[128]; | |
char *p; | |
xstrlcpy(name, _name, sizeof(name)); | |
/* check for option "enc_name//opt1//opt2" */ | |
while ((p = strrstr(name, "//")) != NULL) | |
{ | |
if (_stricmp(p + 2, "nocompat") == 0) | |
use_compat = FALSE; | |
else if (_stricmp(p + 2, "translit") == 0) | |
flag |= FLAG_TRANSLIT; | |
else if (_stricmp(p + 2, "ignore") == 0) | |
flag |= FLAG_IGNORE; | |
*p = 0; | |
} | |
cv.mode = 0; | |
cv.flags = flag; | |
cv.mblen = NULL; | |
cv.flush = NULL; | |
cv.compat = NULL; | |
cv.codepage = name_to_codepage(name); | |
if (cv.codepage == 1200 || cv.codepage == 1201) | |
{ | |
cv.mbtowc = utf16_mbtowc; | |
cv.wctomb = utf16_wctomb; | |
if (_stricmp(name, "UTF-16") == 0 || | |
_stricmp(name, "UTF16") == 0 || | |
_stricmp(name, "UCS-2") == 0 || | |
_stricmp(name, "UCS2") == 0) | |
cv.flags |= FLAG_USE_BOM_ENDIAN; | |
} | |
else if (cv.codepage == 12000 || cv.codepage == 12001) | |
{ | |
cv.mbtowc = utf32_mbtowc; | |
cv.wctomb = utf32_wctomb; | |
if (_stricmp(name, "UTF-32") == 0 || | |
_stricmp(name, "UTF32") == 0 || | |
_stricmp(name, "UCS-4") == 0 || | |
_stricmp(name, "UCS4") == 0) | |
cv.flags |= FLAG_USE_BOM_ENDIAN; | |
} | |
else if (cv.codepage == 65001) | |
{ | |
cv.mbtowc = kernel_mbtowc; | |
cv.wctomb = kernel_wctomb; | |
cv.mblen = utf8_mblen; | |
} | |
else if ((cv.codepage == 50220 || cv.codepage == 50221 || cv.codepage == 50222) && load_mlang()) | |
{ | |
cv.mbtowc = iso2022jp_mbtowc; | |
cv.wctomb = iso2022jp_wctomb; | |
cv.flush = iso2022jp_flush; | |
} | |
else if (cv.codepage == 51932 && load_mlang()) | |
{ | |
cv.mbtowc = mlang_mbtowc; | |
cv.wctomb = mlang_wctomb; | |
cv.mblen = eucjp_mblen; | |
} | |
else if (IsValidCodePage(cv.codepage) | |
&& GetCPInfoEx(cv.codepage, 0, &cpinfoex) != 0) | |
{ | |
cv.mbtowc = kernel_mbtowc; | |
cv.wctomb = kernel_wctomb; | |
if (cpinfoex.MaxCharSize == 1) | |
cv.mblen = sbcs_mblen; | |
else if (cpinfoex.MaxCharSize == 2) | |
cv.mblen = dbcs_mblen; | |
else | |
cv.mblen = mbcs_mblen; | |
} | |
else | |
{ | |
/* not supported */ | |
cv.codepage = -1; | |
} | |
if (use_compat) | |
{ | |
switch (cv.codepage) | |
{ | |
case 932: cv.compat = cp932_compat; break; | |
case 20932: cv.compat = cp20932_compat; break; | |
case 51932: cv.compat = cp51932_compat; break; | |
case 50220: case 50221: case 50222: cv.compat = cp5022x_compat; break; | |
} | |
} | |
return cv; | |
} | |
static int | |
name_to_codepage(const char *name) | |
{ | |
int i; | |
if (*name == '\0' || | |
strcmp(name, "char") == 0) | |
return GetACP(); | |
else if (strcmp(name, "wchar_t") == 0) | |
return 1200; | |
else if (_strnicmp(name, "cp", 2) == 0) | |
return atoi(name + 2); /* CP123 */ | |
else if ('0' <= name[0] && name[0] <= '9') | |
return atoi(name); /* 123 */ | |
else if (_strnicmp(name, "xx", 2) == 0) | |
return atoi(name + 2); /* XX123 for debug */ | |
for (i = 0; codepage_alias[i].name != NULL; ++i) | |
if (_stricmp(name, codepage_alias[i].name) == 0) | |
return codepage_alias[i].codepage; | |
return -1; | |
} | |
/* | |
* http://www.faqs.org/rfcs/rfc2781.html | |
*/ | |
static uint | |
utf16_to_ucs4(const ushort *wbuf) | |
{ | |
uint wc = wbuf[0]; | |
if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) | |
wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000; | |
return wc; | |
} | |
static void | |
ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize) | |
{ | |
if (wc < 0x10000) | |
{ | |
wbuf[0] = wc; | |
*wbufsize = 1; | |
} | |
else | |
{ | |
wc -= 0x10000; | |
wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF); | |
wbuf[1] = 0xDC00 | (wc & 0x3FF); | |
*wbufsize = 2; | |
} | |
} | |
static int | |
is_unicode(int codepage) | |
{ | |
return (codepage == 1200 || codepage == 1201 || | |
codepage == 12000 || codepage == 12001 || | |
codepage == 65000 || codepage == 65001); | |
} | |
/* | |
* Check if codepage is one of those for which the dwFlags parameter | |
* to MultiByteToWideChar() must be zero. Return zero or | |
* MB_ERR_INVALID_CHARS. The docs in Platform SDK for for Windows | |
* Server 2003 R2 claims that also codepage 65001 is one of these, but | |
* that doesn't seem to be the case. The MSDN docs for MSVS2008 leave | |
* out 65001 (UTF-8), and that indeed seems to be the case on XP, it | |
* works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting | |
* from UTF-8. | |
*/ | |
static int | |
mbtowc_flags(int codepage) | |
{ | |
return (codepage == 50220 || codepage == 50221 || | |
codepage == 50222 || codepage == 50225 || | |
codepage == 50227 || codepage == 50229 || | |
codepage == 52936 || codepage == 54936 || | |
(codepage >= 57002 && codepage <= 57011) || | |
codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS; | |
} | |
/* | |
* Check if codepage is one those for which the lpUsedDefaultChar | |
* parameter to WideCharToMultiByte() must be NULL. The docs in | |
* Platform SDK for for Windows Server 2003 R2 claims that this is the | |
* list below, while the MSDN docs for MSVS2008 claim that it is only | |
* for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform | |
* SDK seems to be correct, at least for XP. | |
*/ | |
static int | |
must_use_null_useddefaultchar(int codepage) | |
{ | |
return (codepage == 65000 || codepage == 65001 || | |
codepage == 50220 || codepage == 50221 || | |
codepage == 50222 || codepage == 50225 || | |
codepage == 50227 || codepage == 50229 || | |
codepage == 52936 || codepage == 54936 || | |
(codepage >= 57002 && codepage <= 57011) || | |
codepage == 42); | |
} | |
static void | |
check_utf_bom(rec_iconv_t *cd, ushort *wbuf, int *wbufsize) | |
{ | |
/* If we have a BOM, trust it, despite what the caller said */ | |
if (wbuf[0] == 0xFFFE && (cd->from.flags & FLAG_USE_BOM_ENDIAN)) | |
{ | |
/* swap endian: 1200 <-> 1201 or 12000 <-> 12001 */ | |
cd->from.codepage ^= 1; | |
cd->from.mode |= UNICODE_MODE_SWAPPED; | |
wbuf[0] = 0xFEFF; | |
} | |
/* | |
* Remove BOM. | |
* Don't do this if "to" is Unicode, | |
* except if "to" is UTF-8. | |
*/ | |
if (wbuf[0] == 0xFEFF && (!is_unicode(cd->to.codepage) || cd->to.codepage == 65001)) | |
*wbufsize = 0; | |
} | |
static char * | |
strrstr(const char *str, const char *token) | |
{ | |
int len = strlen(token); | |
const char *p = str + strlen(str); | |
while (str <= --p) | |
if (p[0] == token[0] && strncmp(p, token, len) == 0) | |
return (char *)p; | |
return NULL; | |
} | |
#if defined(USE_LIBICONV_DLL) | |
static int | |
libiconv_iconv_open(rec_iconv_t *cd, const char *fromcode, const char *tocode) | |
{ | |
HMODULE hlibiconv = NULL; | |
HMODULE hmsvcrt = NULL; | |
char dllname[_MAX_PATH]; | |
const char *p; | |
const char *e; | |
f_iconv_open _iconv_open; | |
/* | |
* always try to load dll, so that we can switch dll in runtime. | |
*/ | |
/* XXX: getenv() can't get variable set by SetEnvironmentVariable() */ | |
p = getenv("WINICONV_LIBICONV_DLL"); | |
if (p == NULL) | |
p = DEFAULT_LIBICONV_DLL; | |
/* parse comma separated value */ | |
for ( ; *p != 0; p = (*e == ',') ? e + 1 : e) | |
{ | |
e = strchr(p, ','); | |
if (p == e) | |
continue; | |
else if (e == NULL) | |
e = p + strlen(p); | |
xstrlcpyn(dllname, p, e - p, sizeof(dllname)); | |
hlibiconv = LoadLibrary(dllname); | |
if (hlibiconv != NULL) | |
{ | |
if (hlibiconv == hwiniconv) | |
{ | |
FreeLibrary(hlibiconv); | |
hlibiconv = NULL; | |
continue; | |
} | |
break; | |
} | |
} | |
if (hlastdll != NULL) | |
{ | |
/* decrement reference count */ | |
FreeLibrary(hlastdll); | |
hlastdll = NULL; | |
} | |
if (hlibiconv == NULL) | |
goto failed; | |
hmsvcrt = find_imported_module_by_funcname(hlibiconv, "_errno"); | |
if (hmsvcrt == NULL) | |
goto failed; | |
_iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "libiconv_open"); | |
if (_iconv_open == NULL) | |
_iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "iconv_open"); | |
cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "libiconv_close"); | |
if (cd->iconv_close == NULL) | |
cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "iconv_close"); | |
cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "libiconv"); | |
if (cd->iconv == NULL) | |
cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "iconv"); | |
cd->_errno = (f_errno)GetProcAddress(hmsvcrt, "_errno"); | |
if (_iconv_open == NULL || cd->iconv_close == NULL | |
|| cd->iconv == NULL || cd->_errno == NULL) | |
goto failed; | |
/* increment reference count */ | |
hlastdll = LoadLibrary(dllname); | |
cd->cd = _iconv_open(tocode, fromcode); | |
if (cd->cd == (iconv_t)(-1)) | |
goto failed; | |
cd->hlibiconv = hlibiconv; | |
return TRUE; | |
failed: | |
if (hlibiconv != NULL) | |
FreeLibrary(hlibiconv); | |
/* do not free hmsvcrt which is obtained by GetModuleHandle() */ | |
return FALSE; | |
} | |
/* | |
* Reference: | |
* http://forums.belution.com/ja/vc/000/234/78s.shtml | |
* http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html | |
* | |
* The formal way is | |
* imagehlp.h or dbghelp.h | |
* imagehlp.lib or dbghelp.lib | |
* ImageDirectoryEntryToData() | |
*/ | |
#define TO_DOS_HEADER(base) ((PIMAGE_DOS_HEADER)(base)) | |
#define TO_NT_HEADERS(base) ((PIMAGE_NT_HEADERS)((LPBYTE)(base) + TO_DOS_HEADER(base)->e_lfanew)) | |
static PVOID | |
MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size) | |
{ | |
/* TODO: MappedAsImage? */ | |
PIMAGE_DATA_DIRECTORY p; | |
p = TO_NT_HEADERS(Base)->OptionalHeader.DataDirectory + DirectoryEntry; | |
if (p->VirtualAddress == 0) { | |
*Size = 0; | |
return NULL; | |
} | |
*Size = p->Size; | |
return (PVOID)((LPBYTE)Base + p->VirtualAddress); | |
} | |
static HMODULE | |
find_imported_module_by_funcname(HMODULE hModule, const char *funcname) | |
{ | |
DWORD Base; | |
ULONG Size; | |
PIMAGE_IMPORT_DESCRIPTOR Imp; | |
PIMAGE_THUNK_DATA Name; /* Import Name Table */ | |
PIMAGE_IMPORT_BY_NAME ImpName; | |
Base = (DWORD)hModule; | |
Imp = MyImageDirectoryEntryToData( | |
(LPVOID)Base, | |
TRUE, | |
IMAGE_DIRECTORY_ENTRY_IMPORT, | |
&Size); | |
if (Imp == NULL) | |
return NULL; | |
for ( ; Imp->OriginalFirstThunk != 0; ++Imp) | |
{ | |
Name = (PIMAGE_THUNK_DATA)(Base + Imp->OriginalFirstThunk); | |
for ( ; Name->u1.Ordinal != 0; ++Name) | |
{ | |
if (!IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal)) | |
{ | |
ImpName = (PIMAGE_IMPORT_BY_NAME) | |
(Base + (DWORD)Name->u1.AddressOfData); | |
if (strcmp((char *)ImpName->Name, funcname) == 0) | |
return GetModuleHandle((char *)(Base + Imp->Name)); | |
} | |
} | |
} | |
return NULL; | |
} | |
#endif | |
static int | |
sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) | |
{ | |
return 1; | |
} | |
static int | |
dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) | |
{ | |
int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1; | |
if (bufsize < len) | |
return_error(EINVAL); | |
return len; | |
} | |
static int | |
mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) | |
{ | |
int len = 0; | |
if (cv->codepage == 54936) { | |
if (buf[0] <= 0x7F) len = 1; | |
else if (buf[0] >= 0x81 && buf[0] <= 0xFE && | |
bufsize >= 2 && | |
((buf[1] >= 0x40 && buf[1] <= 0x7E) || | |
(buf[1] >= 0x80 && buf[1] <= 0xFE))) len = 2; | |
else if (buf[0] >= 0x81 && buf[0] <= 0xFE && | |
bufsize >= 4 && | |
buf[1] >= 0x30 && buf[1] <= 0x39) len = 4; | |
else | |
return_error(EINVAL); | |
return len; | |
} | |
else | |
return_error(EINVAL); | |
} | |
static int | |
utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize) | |
{ | |
int len = 0; | |
if (buf[0] < 0x80) len = 1; | |
else if ((buf[0] & 0xE0) == 0xC0) len = 2; | |
else if ((buf[0] & 0xF0) == 0xE0) len = 3; | |
else if ((buf[0] & 0xF8) == 0xF0) len = 4; | |
else if ((buf[0] & 0xFC) == 0xF8) len = 5; | |
else if ((buf[0] & 0xFE) == 0xFC) len = 6; | |
if (len == 0) | |
return_error(EILSEQ); | |
else if (bufsize < len) | |
return_error(EINVAL); | |
return len; | |
} | |
static int | |
eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize) | |
{ | |
if (buf[0] < 0x80) /* ASCII */ | |
return 1; | |
else if (buf[0] == 0x8E) /* JIS X 0201 */ | |
{ | |
if (bufsize < 2) | |
return_error(EINVAL); | |
else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF)) | |
return_error(EILSEQ); | |
return 2; | |
} | |
else if (buf[0] == 0x8F) /* JIS X 0212 */ | |
{ | |
if (bufsize < 3) | |
return_error(EINVAL); | |
else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE) | |
|| !(0xA1 <= buf[2] && buf[2] <= 0xFE)) | |
return_error(EILSEQ); | |
return 3; | |
} | |
else /* JIS X 0208 */ | |
{ | |
if (bufsize < 2) | |
return_error(EINVAL); | |
else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE) | |
|| !(0xA1 <= buf[1] && buf[1] <= 0xFE)) | |
return_error(EILSEQ); | |
return 2; | |
} | |
} | |
static int | |
kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | |
{ | |
int len; | |
len = cv->mblen(cv, buf, bufsize); | |
if (len == -1) | |
return -1; | |
/* If converting from ASCII, reject 8bit | |
* chars. MultiByteToWideChar() doesn't. Note that for ASCII we | |
* know that the mblen function is sbcs_mblen() so len is 1. | |
*/ | |
if (cv->codepage == 20127 && buf[0] >= 0x80) | |
return_error(EILSEQ); | |
*wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage), | |
(const char *)buf, len, (wchar_t *)wbuf, *wbufsize); | |
if (*wbufsize == 0) | |
return_error(EILSEQ); | |
return len; | |
} | |
static int | |
kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | |
{ | |
BOOL usedDefaultChar = 0; | |
BOOL *p = NULL; | |
int flags = 0; | |
int len; | |
if (bufsize == 0) | |
return_error(E2BIG); | |
if (!must_use_null_useddefaultchar(cv->codepage)) | |
{ | |
p = &usedDefaultChar; | |
#ifdef WC_NO_BEST_FIT_CHARS | |
if (!(cv->flags & FLAG_TRANSLIT)) | |
flags |= WC_NO_BEST_FIT_CHARS; | |
#endif | |
} | |
len = WideCharToMultiByte(cv->codepage, flags, | |
(const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p); | |
if (len == 0) | |
{ | |
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) | |
return_error(E2BIG); | |
return_error(EILSEQ); | |
} | |
else if (usedDefaultChar) | |
return_error(EILSEQ); | |
else if (cv->mblen(cv, buf, len) != len) /* validate result */ | |
return_error(EILSEQ); | |
return len; | |
} | |
/* | |
* It seems that the mode (cv->mode) is fixnum. | |
* For example, when converting iso-2022-jp(cp50221) to unicode: | |
* in ascii sequence: mode=0xC42C0000 | |
* in jisx0208 sequence: mode=0xC42C0001 | |
* "C42C" is same for each convert session. | |
* It should be: ((codepage-1)<<16)|state | |
*/ | |
static int | |
mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | |
{ | |
int len; | |
int insize; | |
HRESULT hr; | |
len = cv->mblen(cv, buf, bufsize); | |
if (len == -1) | |
return -1; | |
insize = len; | |
hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage, | |
(const char *)buf, &insize, (wchar_t *)wbuf, wbufsize); | |
if (hr != S_OK || insize != len) | |
return_error(EILSEQ); | |
return len; | |
} | |
static int | |
mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | |
{ | |
char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */ | |
int tmpsize = MB_CHAR_MAX; | |
int insize = wbufsize; | |
HRESULT hr; | |
hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage, | |
(const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize); | |
if (hr != S_OK || insize != wbufsize) | |
return_error(EILSEQ); | |
else if (bufsize < tmpsize) | |
return_error(E2BIG); | |
else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize) | |
return_error(EILSEQ); | |
memcpy(buf, tmpbuf, tmpsize); | |
return tmpsize; | |
} | |
static int | |
utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | |
{ | |
if (bufsize < 2) | |
return_error(EINVAL); | |
if (cv->codepage == 1200) /* little endian */ | |
wbuf[0] = (buf[1] << 8) | buf[0]; | |
else if (cv->codepage == 1201) /* big endian */ | |
wbuf[0] = (buf[0] << 8) | buf[1]; | |
if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF) | |
return_error(EILSEQ); | |
if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) | |
{ | |
if (bufsize < 4) | |
return_error(EINVAL); | |
if (cv->codepage == 1200) /* little endian */ | |
wbuf[1] = (buf[3] << 8) | buf[2]; | |
else if (cv->codepage == 1201) /* big endian */ | |
wbuf[1] = (buf[2] << 8) | buf[3]; | |
if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF)) | |
return_error(EILSEQ); | |
*wbufsize = 2; | |
return 4; | |
} | |
*wbufsize = 1; | |
return 2; | |
} | |
static int | |
utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | |
{ | |
if (bufsize < 2) | |
return_error(E2BIG); | |
if (cv->codepage == 1200) /* little endian */ | |
{ | |
buf[0] = (wbuf[0] & 0x00FF); | |
buf[1] = (wbuf[0] & 0xFF00) >> 8; | |
} | |
else if (cv->codepage == 1201) /* big endian */ | |
{ | |
buf[0] = (wbuf[0] & 0xFF00) >> 8; | |
buf[1] = (wbuf[0] & 0x00FF); | |
} | |
if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) | |
{ | |
if (bufsize < 4) | |
return_error(E2BIG); | |
if (cv->codepage == 1200) /* little endian */ | |
{ | |
buf[2] = (wbuf[1] & 0x00FF); | |
buf[3] = (wbuf[1] & 0xFF00) >> 8; | |
} | |
else if (cv->codepage == 1201) /* big endian */ | |
{ | |
buf[2] = (wbuf[1] & 0xFF00) >> 8; | |
buf[3] = (wbuf[1] & 0x00FF); | |
} | |
return 4; | |
} | |
return 2; | |
} | |
static int | |
utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | |
{ | |
uint wc; | |
if (bufsize < 4) | |
return_error(EINVAL); | |
if (cv->codepage == 12000) /* little endian */ | |
wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | |
else if (cv->codepage == 12001) /* big endian */ | |
wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | |
if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc) | |
return_error(EILSEQ); | |
ucs4_to_utf16(wc, wbuf, wbufsize); | |
return 4; | |
} | |
static int | |
utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | |
{ | |
uint wc; | |
if (bufsize < 4) | |
return_error(E2BIG); | |
wc = utf16_to_ucs4(wbuf); | |
if (cv->codepage == 12000) /* little endian */ | |
{ | |
buf[0] = wc & 0x000000FF; | |
buf[1] = (wc & 0x0000FF00) >> 8; | |
buf[2] = (wc & 0x00FF0000) >> 16; | |
buf[3] = (wc & 0xFF000000) >> 24; | |
} | |
else if (cv->codepage == 12001) /* big endian */ | |
{ | |
buf[0] = (wc & 0xFF000000) >> 24; | |
buf[1] = (wc & 0x00FF0000) >> 16; | |
buf[2] = (wc & 0x0000FF00) >> 8; | |
buf[3] = wc & 0x000000FF; | |
} | |
return 4; | |
} | |
/* | |
* 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) | |
* 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow | |
* 1 byte Kana) | |
* 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte | |
* Kana - SO/SI) | |
* | |
* MultiByteToWideChar() and WideCharToMultiByte() behave differently | |
* depending on Windows version. On XP, WideCharToMultiByte() doesn't | |
* terminate result sequence with ascii escape. But Vista does. | |
* Use MLang instead. | |
*/ | |
#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift)) | |
#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF) | |
#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF) | |
#define ISO2022_SI 0 | |
#define ISO2022_SO 1 | |
/* shift in */ | |
static const char iso2022_SI_seq[] = "\x0F"; | |
/* shift out */ | |
static const char iso2022_SO_seq[] = "\x0E"; | |
typedef struct iso2022_esc_t iso2022_esc_t; | |
struct iso2022_esc_t { | |
const char *esc; | |
int esc_len; | |
int len; | |
int cs; | |
}; | |
#define ISO2022JP_CS_ASCII 0 | |
#define ISO2022JP_CS_JISX0201_ROMAN 1 | |
#define ISO2022JP_CS_JISX0201_KANA 2 | |
#define ISO2022JP_CS_JISX0208_1978 3 | |
#define ISO2022JP_CS_JISX0208_1983 4 | |
#define ISO2022JP_CS_JISX0212 5 | |
static iso2022_esc_t iso2022jp_esc[] = { | |
{"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII}, | |
{"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN}, | |
{"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA}, | |
{"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */ | |
{"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983}, | |
{"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212}, | |
{NULL, 0, 0, 0} | |
}; | |
static int | |
iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | |
{ | |
iso2022_esc_t *iesc = iso2022jp_esc; | |
char tmp[MB_CHAR_MAX]; | |
int insize; | |
HRESULT hr; | |
DWORD dummy = 0; | |
int len; | |
int esc_len; | |
int cs; | |
int shift; | |
int i; | |
if (buf[0] == 0x1B) | |
{ | |
for (i = 0; iesc[i].esc != NULL; ++i) | |
{ | |
esc_len = iesc[i].esc_len; | |
if (bufsize < esc_len) | |
{ | |
if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0) | |
return_error(EINVAL); | |
} | |
else | |
{ | |
if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0) | |
{ | |
cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI); | |
*wbufsize = 0; | |
return esc_len; | |
} | |
} | |
} | |
/* not supported escape sequence */ | |
return_error(EILSEQ); | |
} | |
else if (buf[0] == iso2022_SO_seq[0]) | |
{ | |
cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO); | |
*wbufsize = 0; | |
return 1; | |
} | |
else if (buf[0] == iso2022_SI_seq[0]) | |
{ | |
cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI); | |
*wbufsize = 0; | |
return 1; | |
} | |
cs = ISO2022_MODE_CS(cv->mode); | |
shift = ISO2022_MODE_SHIFT(cv->mode); | |
/* reset the mode for informal sequence */ | |
if (buf[0] < 0x20) | |
{ | |
cs = ISO2022JP_CS_ASCII; | |
shift = ISO2022_SI; | |
} | |
len = iesc[cs].len; | |
if (bufsize < len) | |
return_error(EINVAL); | |
for (i = 0; i < len; ++i) | |
if (!(buf[i] < 0x80)) | |
return_error(EILSEQ); | |
esc_len = iesc[cs].esc_len; | |
memcpy(tmp, iesc[cs].esc, esc_len); | |
if (shift == ISO2022_SO) | |
{ | |
memcpy(tmp + esc_len, iso2022_SO_seq, 1); | |
esc_len += 1; | |
} | |
memcpy(tmp + esc_len, buf, len); | |
if ((cv->codepage == 50220 || cv->codepage == 50221 | |
|| cv->codepage == 50222) && shift == ISO2022_SO) | |
{ | |
/* XXX: shift-out cannot be used for mbtowc (both kernel and | |
* mlang) */ | |
esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len; | |
memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len); | |
memcpy(tmp + esc_len, buf, len); | |
} | |
insize = len + esc_len; | |
hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage, | |
(const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize); | |
if (hr != S_OK || insize != len + esc_len) | |
return_error(EILSEQ); | |
/* Check for conversion error. Assuming defaultChar is 0x3F. */ | |
/* ascii should be converted from ascii */ | |
if (wbuf[0] == buf[0] | |
&& cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) | |
return_error(EILSEQ); | |
/* reset the mode for informal sequence */ | |
if (cv->mode != ISO2022_MODE(cs, shift)) | |
cv->mode = ISO2022_MODE(cs, shift); | |
return len; | |
} | |
static int | |
iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | |
{ | |
iso2022_esc_t *iesc = iso2022jp_esc; | |
char tmp[MB_CHAR_MAX]; | |
int tmpsize = MB_CHAR_MAX; | |
int insize = wbufsize; | |
HRESULT hr; | |
DWORD dummy = 0; | |
int len; | |
int esc_len; | |
int cs; | |
int shift; | |
int i; | |
/* | |
* MultiByte = [escape sequence] + character + [escape sequence] | |
* | |
* Whether trailing escape sequence is added depends on which API is | |
* used (kernel or MLang, and its version). | |
*/ | |
hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage, | |
(const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize); | |
if (hr != S_OK || insize != wbufsize) | |
return_error(EILSEQ); | |
else if (bufsize < tmpsize) | |
return_error(E2BIG); | |
if (tmpsize == 1) | |
{ | |
cs = ISO2022JP_CS_ASCII; | |
esc_len = 0; | |
} | |
else | |
{ | |
for (i = 1; iesc[i].esc != NULL; ++i) | |
{ | |
esc_len = iesc[i].esc_len; | |
if (strncmp(tmp, iesc[i].esc, esc_len) == 0) | |
{ | |
cs = iesc[i].cs; | |
break; | |
} | |
} | |
if (iesc[i].esc == NULL) | |
/* not supported escape sequence */ | |
return_error(EILSEQ); | |
} | |
shift = ISO2022_SI; | |
if (tmp[esc_len] == iso2022_SO_seq[0]) | |
{ | |
shift = ISO2022_SO; | |
esc_len += 1; | |
} | |
len = iesc[cs].len; | |
/* Check for converting error. Assuming defaultChar is 0x3F. */ | |
/* ascii should be converted from ascii */ | |
if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80)) | |
return_error(EILSEQ); | |
else if (tmpsize < esc_len + len) | |
return_error(EILSEQ); | |
if (cv->mode == ISO2022_MODE(cs, shift)) | |
{ | |
/* remove escape sequence */ | |
if (esc_len != 0) | |
memmove(tmp, tmp + esc_len, len); | |
esc_len = 0; | |
} | |
else | |
{ | |
if (cs == ISO2022JP_CS_ASCII) | |
{ | |
esc_len = iesc[ISO2022JP_CS_ASCII].esc_len; | |
memmove(tmp + esc_len, tmp, len); | |
memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len); | |
} | |
if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO) | |
{ | |
/* shift-in before changing to other mode */ | |
memmove(tmp + 1, tmp, len + esc_len); | |
memcpy(tmp, iso2022_SI_seq, 1); | |
esc_len += 1; | |
} | |
} | |
if (bufsize < len + esc_len) | |
return_error(E2BIG); | |
memcpy(buf, tmp, len + esc_len); | |
cv->mode = ISO2022_MODE(cs, shift); | |
return len + esc_len; | |
} | |
static int | |
iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize) | |
{ | |
iso2022_esc_t *iesc = iso2022jp_esc; | |
int esc_len; | |
if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) | |
{ | |
esc_len = 0; | |
if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) | |
esc_len += 1; | |
if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) | |
esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; | |
if (bufsize < esc_len) | |
return_error(E2BIG); | |
esc_len = 0; | |
if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) | |
{ | |
memcpy(buf, iso2022_SI_seq, 1); | |
esc_len += 1; | |
} | |
if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) | |
{ | |
memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc, | |
iesc[ISO2022JP_CS_ASCII].esc_len); | |
esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; | |
} | |
return esc_len; | |
} | |
return 0; | |
} | |
#if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL) | |
BOOL WINAPI | |
DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) | |
{ | |
switch( fdwReason ) | |
{ | |
case DLL_PROCESS_ATTACH: | |
hwiniconv = (HMODULE)hinstDLL; | |
break; | |
case DLL_THREAD_ATTACH: | |
case DLL_THREAD_DETACH: | |
case DLL_PROCESS_DETACH: | |
break; | |
} | |
return TRUE; | |
} | |
#endif | |
#if defined(MAKE_EXE) | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <io.h> | |
int | |
main(int argc, char **argv) | |
{ | |
char *fromcode = NULL; | |
char *tocode = NULL; | |
int i; | |
char inbuf[BUFSIZ]; | |
char outbuf[BUFSIZ]; | |
const char *pin; | |
char *pout; | |
size_t inbytesleft; | |
size_t outbytesleft; | |
size_t rest = 0; | |
iconv_t cd; | |
size_t r; | |
FILE *in = stdin; | |
_setmode(_fileno(stdin), _O_BINARY); | |
_setmode(_fileno(stdout), _O_BINARY); | |
for (i = 1; i < argc; ++i) | |
{ | |
if (strcmp(argv[i], "-l") == 0) | |
{ | |
for (i = 0; codepage_alias[i].name != NULL; ++i) | |
printf("%s\n", codepage_alias[i].name); | |
return 0; | |
} | |
if (strcmp(argv[i], "-f") == 0) | |
fromcode = argv[++i]; | |
else if (strcmp(argv[i], "-t") == 0) | |
tocode = argv[++i]; | |
else | |
{ | |
in = fopen(argv[i], "rb"); | |
if (in == NULL) | |
{ | |
fprintf(stderr, "cannot open %s\n", argv[i]); | |
return 1; | |
} | |
break; | |
} | |
} | |
if (fromcode == NULL || tocode == NULL) | |
{ | |
printf("usage: %s -f from-enc -t to-enc [file]\n", argv[0]); | |
return 0; | |
} | |
cd = iconv_open(tocode, fromcode); | |
if (cd == (iconv_t)(-1)) | |
{ | |
perror("iconv_open error"); | |
return 1; | |
} | |
while ((inbytesleft = fread(inbuf + rest, 1, sizeof(inbuf) - rest, in)) != 0 | |
|| rest != 0) | |
{ | |
inbytesleft += rest; | |
pin = inbuf; | |
pout = outbuf; | |
outbytesleft = sizeof(outbuf); | |
r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft); | |
fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); | |
if (r == (size_t)(-1) && errno != EINVAL && errno != E2BIG) | |
{ | |
perror("conversion error"); | |
return 1; | |
} | |
memmove(inbuf, pin, inbytesleft); | |
rest = inbytesleft; | |
} | |
pout = outbuf; | |
outbytesleft = sizeof(outbuf); | |
r = iconv(cd, NULL, NULL, &pout, &outbytesleft); | |
fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); | |
if (r == (size_t)(-1)) | |
{ | |
perror("conversion error"); | |
return 1; | |
} | |
iconv_close(cd); | |
return 0; | |
} | |
#endif | |