#include #include #include #include #include #include #include #include #include #include #include #include "WinCompat.h" #include "WinPort.h" #include "wineguts.h" #include "PathHelpers.h" #include "UtfConvert.hpp" template int TranscodeUTF(int flags, const SRC_T *src, size_t srclen, DST_T *dst, size_t dstlen) { const bool fail_on_illformed = ((flags & MB_ERR_INVALID_CHARS) != 0); if (dstlen == 0) { DummyPushBack pb; try { const unsigned ucr = UtfConvert(src, srclen, pb, fail_on_illformed); if (ucr & (CONV_ILLFORMED_CHARS | CONV_NEED_MORE_SRC)) { WINPORT(SetLastError)( ERROR_NO_UNICODE_TRANSLATION ); } } catch (std::exception &e) { fprintf(stderr, "%s: %s\n", __FUNCTION__, e.what()); WINPORT(SetLastError)( ERROR_NO_UNICODE_TRANSLATION ); } return (int)pb.size(); } ArrayPushBack pb(dst, dst + dstlen); try { const unsigned ucr = UtfConvert(src, srclen, pb, fail_on_illformed); if (ucr & (CONV_ILLFORMED_CHARS | CONV_NEED_MORE_SRC)) { WINPORT(SetLastError)( ERROR_NO_UNICODE_TRANSLATION ); } else if (ucr & CONV_NEED_MORE_DST) { WINPORT(SetLastError)( ERROR_INSUFFICIENT_BUFFER ); return 0; } } catch (ArrayPushBackOverflow &e) { (void)e; WINPORT(SetLastError)( ERROR_INSUFFICIENT_BUFFER ); return 0; } catch (std::exception &e) { fprintf(stderr, "%s: %s\n", __FUNCTION__, e.what()); WINPORT(SetLastError)( ERROR_NO_UNICODE_TRANSLATION ); } return (int)pb.size(); } template static int Wide2Bytes( int flags, const WCHAR *src, int srclen, void *dst, int dstlen) { if (srclen < 0) { // per MSDN - conversion should include terminating NUL char srclen = tzlen(src) + 1; } if (dstlen > 0) { dstlen/= sizeof(BYTES_TYPE); // if dstlen specified was nonzero but obviousy too small then fail fast if (dstlen == 0 || (sizeof(BYTES_TYPE) == sizeof(WCHAR) && dstlen < srclen)) { WINPORT(SetLastError)( ERROR_INSUFFICIENT_BUFFER ); return 0; } } else { dstlen = 0; } int r; if (sizeof(BYTES_TYPE) == sizeof(WCHAR)) { // noop shortcut if (dstlen) { memcpy(dst, src, srclen * sizeof(WCHAR)); } r = srclen; } else { r = TranscodeUTF(flags, src, srclen, (BYTES_TYPE *)dst, dstlen); } if (r > 0) { if (BYTEREV && dstlen) { RevBytes((BYTES_TYPE *)dst, r); } r*= sizeof(BYTES_TYPE); } return r; } template static int Bytes2Wide( int flags, const void *src, int srclen, WCHAR *dst, int dstlen) { if (srclen < 0) { // per MSDN - conversion should include terminating NUL char srclen = tzlen((const BYTES_TYPE*)src) + 1; } else { srclen/= sizeof(BYTES_TYPE); } if (dstlen < 0) { dstlen = 0; } if (sizeof(BYTES_TYPE) == sizeof(WCHAR)) { // noop shortcut if (dstlen != 0) { if (dstlen < srclen) { WINPORT(SetLastError)( ERROR_INSUFFICIENT_BUFFER ); return 0; } memcpy(dst, src, srclen * sizeof(WCHAR)); } return srclen; } if (BYTEREV && srclen > 0) { StackHeapArray reversed_src(srclen); RevBytes(reversed_src.Get(), (const BYTES_TYPE *)src, reversed_src.Count()); return TranscodeUTF(flags, reversed_src.Get(), reversed_src.Count(), dst, dstlen); } return TranscodeUTF(flags, (const BYTES_TYPE *)src, srclen, dst, dstlen); } struct Codepages { int oem; int ansi; }; static Codepages DeduceCodepages() { // deduce oem/ansi cp from system locale std::ifstream is; is.open(InMyConfig("cp").c_str()); if (is.is_open()) { Codepages out {866, 1251}; std::string str; getline (is, str); if (!str.empty()) { out.oem = atoi(str.c_str()); } getline (is, str); if (!str.empty()) { out.ansi = atoi(str.c_str()); } return out; } const char *lc = setlocale(LC_CTYPE, NULL); if (!lc) { fprintf(stderr, "DeduceCodepages: setlocale returned NULL\n"); return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "af_ZA")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "ar_SA")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_LB")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_EG")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_DZ")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_BH")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_IQ")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_JO")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_KW")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_LY")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_MA")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_OM")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_QA")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_SY")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_TN")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_AE")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ar_YE")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "ast_ES")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "az_AZ")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "az_AZ")) { return Codepages{857, 1254}; } if (IsLocaleMatches(lc, "be_BY")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "bg_BG")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "br_FR")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "ca_ES")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "zh_CN")) { return Codepages{936, 936}; } if (IsLocaleMatches(lc, "zh_TW")) { return Codepages{950, 950}; } if (IsLocaleMatches(lc, "kw_GB")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "cs_CZ")) { return Codepages{852, 1250}; } if (IsLocaleMatches(lc, "cy_GB")) { return Codepages{850, 28604}; } if (IsLocaleMatches(lc, "da_DK")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "de_AT")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "de_LI")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "de_LU")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "de_CH")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "de_DE")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "el_GR")) { return Codepages{737, 1253}; } if (IsLocaleMatches(lc, "en_AU")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "en_CA")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "en_GB")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "en_IE")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "en_JM")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "en_BZ")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "en_PH")) { return Codepages{437, 1252}; } if (IsLocaleMatches(lc, "en_ZA")) { return Codepages{437, 1252}; } if (IsLocaleMatches(lc, "en_TT")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "en_US")) { return Codepages{437, 1252}; } if (IsLocaleMatches(lc, "en_ZW")) { return Codepages{437, 1252}; } if (IsLocaleMatches(lc, "en_NZ")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_PA")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_BO")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_CR")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_DO")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_SV")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_EC")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_GT")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_HN")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_NI")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_CL")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_MX")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_ES")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_CO")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_ES")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_PE")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_AR")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_PR")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_VE")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_UY")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "es_PY")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "et_EE")) { return Codepages{775, 1257}; } if (IsLocaleMatches(lc, "eu_ES")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "fa_IR")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "fi_FI")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "fo_FO")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "fr_FR")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "fr_BE")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "fr_CA")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "fr_LU")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "fr_MC")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "fr_CH")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "ga_IE")) { return Codepages{437, 1252}; } if (IsLocaleMatches(lc, "gd_GB")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "gv_IM")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "gl_ES")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "he_IL")) { return Codepages{862, 1255}; } if (IsLocaleMatches(lc, "hr_HR")) { return Codepages{852, 1250}; } if (IsLocaleMatches(lc, "hu_HU")) { return Codepages{852, 1250}; } if (IsLocaleMatches(lc, "id_ID")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "is_IS")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "it_IT")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "it_CH")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "iv_IV")) { return Codepages{437, 1252}; } if (IsLocaleMatches(lc, "ja_JP")) { return Codepages{932, 932}; } if (IsLocaleMatches(lc, "kk_KZ")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "ko_KR")) { return Codepages{949, 949}; } if (IsLocaleMatches(lc, "ky_KG")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "lt_LT")) { return Codepages{775, 1251}; } if (IsLocaleMatches(lc, "lv_LV")) { return Codepages{775, 1257}; } if (IsLocaleMatches(lc, "mk_MK")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "mn_MN")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "ms_BN")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "ms_MY")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "nl_BE")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "nl_NL")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "nl_SR")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "nn_NO")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "nb_NO")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "pl_PL")) { return Codepages{852, 1250}; } if (IsLocaleMatches(lc, "pt_BR")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "pt_PT")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "rm_CH")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "ro_RO")) { return Codepages{852, 1250}; } if (IsLocaleMatches(lc, "ru_RU")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "sk_SK")) { return Codepages{852, 1250}; } if (IsLocaleMatches(lc, "sl_SI")) { return Codepages{852, 1250}; } if (IsLocaleMatches(lc, "sq_AL")) { return Codepages{852, 1250}; } if (IsLocaleMatches(lc, "sr_RS")) { return Codepages{855, 1251}; } if (IsLocaleMatches(lc, "sr_RS")) { return Codepages{852, 1250}; } if (IsLocaleMatches(lc, "sv_SE")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "sv_FI")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "sw_KE")) { return Codepages{437, 1252}; } if (IsLocaleMatches(lc, "th_TH")) { return Codepages{874, 874}; } if (IsLocaleMatches(lc, "tr_TR")) { return Codepages{857, 1254}; } if (IsLocaleMatches(lc, "tt_RU")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "uk_UA")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "ur_PK")) { return Codepages{720, 1256}; } if (IsLocaleMatches(lc, "uz_UZ")) { return Codepages{866, 1251}; } if (IsLocaleMatches(lc, "uz_UZ")) { return Codepages{857, 1254}; } if (IsLocaleMatches(lc, "vi_VN")) { return Codepages{1258, 1258}; } if (IsLocaleMatches(lc, "wa_BE")) { return Codepages{850, 1252}; } if (IsLocaleMatches(lc, "zh_HK")) { return Codepages{950, 950}; } if (IsLocaleMatches(lc, "zh_SG")) { return Codepages{936, 936}; } if (IsLocaleMatches(lc, "zh_MO")) { return Codepages{950, 1252}; } if (!IsLocaleMatches(lc, "C") && !IsLocaleMatches(lc, "UTF8")) { fprintf(stderr, "DeduceCodepages: unknown locale '%s'\n", lc); } return Codepages{866, 1251}; } static UINT TranslateCodepage(UINT codepage) { static Codepages s_cp = DeduceCodepages(); switch (codepage) { case CP_ACP: return s_cp.ansi; case CP_OEMCP: return s_cp.oem; default: return codepage; } } extern "C" { /*********************************************************************** * utf7_write_w * * Helper for utf7_mbstowcs * * RETURNS * TRUE on success, FALSE on error */ static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character) { if (dstlen > 0) { if (*index >= dstlen) return FALSE; dst[*index] = character; } (*index)++; return TRUE; } /*********************************************************************** * utf7_mbstowcs * * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar * * RETURNS * On success, the number of characters written * On dst buffer overflow, -1 */ static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen) { static const signed char base64_decoding_table[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */ }; const char *source_end = src + srclen; int dest_index = 0; DWORD byte_pair = 0; short offset = 0; while (src < source_end) { if (*src == '+') { src++; if (src >= source_end) break; if (*src == '-') { /* just a plus sign escaped as +- */ if (!utf7_write_w(dst, dstlen, &dest_index, '+')) return -1; src++; continue; } do { signed char sextet = *src; if (sextet == '-') { /* skip over the dash and end base64 decoding * the current, unfinished byte pair is discarded */ src++; offset = 0; break; } if (sextet < 0) { /* the next character of src is < 0 and therefore not part of a base64 sequence * the current, unfinished byte pair is NOT discarded in this case * this is probably a bug in Windows */ break; } sextet = base64_decoding_table[sextet]; if (sextet == -1) { /* -1 means that the next character of src is not part of a base64 sequence * in other words, all sextets in this base64 sequence have been processed * the current, unfinished byte pair is discarded */ offset = 0; break; } byte_pair = (byte_pair << 6) | sextet; offset += 6; if (offset >= 16) { /* this byte pair is done */ if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF)) return -1; offset -= 16; } src++; } while (src < source_end); } else { /* we have to convert to unsigned char in case *src < 0 */ if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src)) return -1; src++; } } return dest_index; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /*********************************************************************** * MultiByteToWideChar (KERNEL32.@) * * Convert a multibyte character string into a Unicode string. * * PARAMS * page [I] Codepage character set to convert from * flags [I] Character mapping flags * src [I] Source string buffer * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated * dst [O] Destination buffer * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length * * RETURNS * Success: If dstlen > 0, the number of characters written to dst. * If dstlen == 0, the number of characters needed to perform the * conversion. In both cases the count includes the terminating NUL. * Failure: 0. Use GetLastError() to determine the cause. Possible errors are * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is * possible for src. */ WINPORT_DECL(MultiByteToWideChar, int, ( UINT page, DWORD flags, LPCSTR src, int srclen, LPWSTR dst, int dstlen)) { //return ::MultiByteToWideChar(page, flags, src, srclen, dst, dstlen); //fprintf(stderr, "MultiByteToWideChar\n"); const union cptable *table; int ret; page = TranslateCodepage(page); if (!src || !srclen || (!dst && dstlen) || dstlen < 0) { WINPORT(SetLastError)( ERROR_INVALID_PARAMETER ); return 0; } if (srclen < 0) srclen = strlen(src) + 1; WINPORT(SetLastError)( ERROR_SUCCESS ); switch(page) { case CP_SYMBOL: if (flags) { WINPORT(SetLastError)( ERROR_INVALID_FLAGS ); return 0; } ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen ); break; case CP_UTF7: if (flags) { WINPORT(SetLastError)( ERROR_INVALID_FLAGS ); return 0; } ret = utf7_mbstowcs( src, srclen, dst, dstlen ); break; case CP_UTF16LE: ret = Bytes2Wide( flags, src, srclen, dst, dstlen ); break; case CP_UTF16BE: ret = Bytes2Wide( flags, src, srclen, dst, dstlen ); break; case CP_UTF32LE: ret = Bytes2Wide( flags, src, srclen, dst, dstlen ); break; case CP_UTF32BE: ret = Bytes2Wide( flags, src, srclen, dst, dstlen ); break; case CP_UTF8: ret = Bytes2Wide( flags, src, srclen, dst, dstlen); break; default: if (!(table = get_codepage_table( page ))) { fprintf(stderr, "MultiByteToWideChar: bad codepage %x\n", page); WINPORT(SetLastError)( ERROR_INVALID_PARAMETER ); return 0; } ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen ); break; } if (ret <= 0) { switch(ret) { case -1: WINPORT(SetLastError)( ERROR_INSUFFICIENT_BUFFER ); break; case -2: WINPORT(SetLastError)( ERROR_NO_UNICODE_TRANSLATION ); break; } ret = 0; } return ret; } /*********************************************************************** * utf7_can_directly_encode * * Helper for utf7_wcstombs */ static inline BOOL utf7_can_directly_encode(WCHAR codepoint) { static const BOOL directly_encodable_table[] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */ 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */ }; return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE; } /*********************************************************************** * utf7_write_c * * Helper for utf7_wcstombs * * RETURNS * TRUE on success, FALSE on error */ static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character) { if (dstlen > 0) { if (*index >= dstlen) return FALSE; dst[*index] = character; } (*index)++; return TRUE; } /*********************************************************************** * utf7_wcstombs * * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte * * RETURNS * On success, the number of characters written * On dst buffer overflow, -1 */ static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen) { static const char base64_encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const WCHAR *source_end = src + srclen; int dest_index = 0; while (src < source_end) { if (*src == '+') { if (!utf7_write_c(dst, dstlen, &dest_index, '+')) return -1; if (!utf7_write_c(dst, dstlen, &dest_index, '-')) return -1; src++; } else if (utf7_can_directly_encode(*src)) { if (!utf7_write_c(dst, dstlen, &dest_index, *src)) return -1; src++; } else { unsigned int offset = 0; DWORD byte_pair = 0; if (!utf7_write_c(dst, dstlen, &dest_index, '+')) return -1; while (src < source_end && !utf7_can_directly_encode(*src)) { byte_pair = (byte_pair << 16) | *src; offset += 16; while (offset >= 6) { if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F])) return -1; offset -= 6; } src++; } if (offset) { /* Windows won't create a padded base64 character if there's no room for the - sign * as well ; this is probably a bug in Windows */ if (dstlen > 0 && dest_index + 1 >= dstlen) return -1; byte_pair <<= (6 - offset); if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F])) return -1; } /* Windows always explicitly terminates the base64 sequence even though RFC 2152 (page 3, rule 2) does not require this */ if (!utf7_write_c(dst, dstlen, &dest_index, '-')) return -1; } } return dest_index; } /*********************************************************************** * WideCharToMultiByte (KERNEL32.@) * * Convert a Unicode character string into a multibyte string. * * PARAMS * page [I] Code page character set to convert to * flags [I] Mapping Flags (MB_ constants from "winnls.h"). * src [I] Source string buffer * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated * dst [O] Destination buffer * dstlen [I] Length of dst (in bytes), or 0 to compute the required length * defchar [I] Default character to use for conversion if no exact * conversion can be made * used [O] Set if default character was used in the conversion * * RETURNS * Success: If dstlen > 0, the number of characters written to dst. * If dstlen == 0, number of characters needed to perform the * conversion. In both cases the count includes the terminating NUL. * Failure: 0. Use GetLastError() to determine the cause. Possible errors are * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid * parameter was given. */ WINPORT_DECL(WideCharToMultiByte, int, ( UINT page, DWORD flags, LPCWSTR src, int srclen, LPSTR dst, int dstlen, LPCSTR defchar, LPBOOL used)) { //return ::WideCharToMultiByte(page, flags, src, srclen, dst, dstlen, defchar, used); //fprintf(stderr, "WideCharToMultiByte\n"); const union cptable *table; int ret, used_tmp; page = TranslateCodepage(page); if (!src || !srclen || (!dst && dstlen) || dstlen < 0) { WINPORT(SetLastError)( ERROR_INVALID_PARAMETER ); return 0; } if (srclen < 0) srclen = strlenW(src) + 1; WINPORT(SetLastError)( ERROR_SUCCESS ); switch(page) { case CP_SYMBOL: /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */ if (flags) { WINPORT(SetLastError)( ERROR_INVALID_FLAGS ); return 0; } if (defchar || used) { WINPORT(SetLastError)( ERROR_INVALID_PARAMETER ); return 0; } ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen ); break; case CP_UTF7: /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */ if (defchar || used) { WINPORT(SetLastError)( ERROR_INVALID_PARAMETER ); return 0; } if (flags) { WINPORT(SetLastError)( ERROR_INVALID_FLAGS ); return 0; } ret = utf7_wcstombs( src, srclen, dst, dstlen ); break; case CP_UTF16LE: ret = Wide2Bytes( flags, src, srclen, dst, dstlen ); break; case CP_UTF16BE: ret = Wide2Bytes( flags, src, srclen, dst, dstlen ); break; case CP_UTF32LE: ret = Wide2Bytes( flags, src, srclen, dst, dstlen ); break; case CP_UTF32BE: ret = Wide2Bytes( flags, src, srclen, dst, dstlen ); break; case CP_UTF8: if (defchar || used) { fprintf(stderr, "WideCharToMultiByte: 'defchar' and 'used' can't be used with UTF8\n"); WINPORT(SetLastError)( ERROR_INVALID_PARAMETER ); return 0; } ret = Wide2Bytes( flags, src, srclen, dst, dstlen ); break; default: if (!(table = get_codepage_table( page ))) { fprintf(stderr, "WideCharToMultiByte: bad codepage %x\n", page); WINPORT(SetLastError)( ERROR_INVALID_PARAMETER ); return 0; } ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen, defchar, used ? &used_tmp : NULL ); if (used) *used = used_tmp; break; } if (ret < 0) { switch (ret) { case -1: WINPORT(SetLastError)( ERROR_INSUFFICIENT_BUFFER ); break; case -2: WINPORT(SetLastError)( ERROR_NO_UNICODE_TRANSLATION ); break; } ret = 0; } return ret; } WINPORT_DECL(GetOEMCP, UINT, ()) { return TranslateCodepage(CP_OEMCP);//get_codepage_table( 866 )->info.codepage;//866 } WINPORT_DECL(GetACP, UINT, ()) { return TranslateCodepage(CP_ACP);//get_codepage_table( 1251 )->info.codepage;//1251 } WINPORT_DECL(GetCPInfo, BOOL, (UINT codepage, LPCPINFO cpinfo)) { const union cptable *table; if (!cpinfo) { WINPORT(SetLastError)( ERROR_INVALID_PARAMETER ); return FALSE; } memset(cpinfo, 0, sizeof(*cpinfo)); codepage = TranslateCodepage(codepage); if (!(table = get_codepage_table( codepage ))) { switch(codepage) { case CP_UTF7: case CP_UTF8: cpinfo->DefaultChar[0] = 0x3f; cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4; return TRUE; case CP_UTF16LE: cpinfo->DefaultChar[0] = 0x3f; cpinfo->MaxCharSize = 2; return TRUE; case CP_UTF16BE: cpinfo->DefaultChar[1] = 0x3f; cpinfo->MaxCharSize = 2; return TRUE; case CP_UTF32LE: cpinfo->DefaultChar[0] = 0x3f; cpinfo->MaxCharSize = 4; return TRUE; case CP_UTF32BE: cpinfo->DefaultChar[3] = 0x3f; cpinfo->MaxCharSize = 4; return TRUE; } fprintf(stderr, "GetCPInfo: bad codepage %u\n", codepage); WINPORT(SetLastError)( ERROR_INVALID_PARAMETER ); return FALSE; } if (table->info.def_char & 0xff00) { cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8; cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff; } else { cpinfo->DefaultChar[0] = table->info.def_char & 0xff; cpinfo->DefaultChar[1] = 0; } if ((cpinfo->MaxCharSize = table->info.char_size) == 2) memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) ); else cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0; return TRUE; } WINPORT_DECL(GetCPInfoEx, BOOL, (UINT codepage, DWORD dwFlags, LPCPINFOEX cpinfo)) { if (!WINPORT(GetCPInfo)( codepage, (LPCPINFO)cpinfo )) return FALSE; switch(codepage) { case CP_UTF7: { static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0}; cpinfo->CodePage = CP_UTF7; cpinfo->UnicodeDefaultChar = 0x3f; strcpyW(cpinfo->CodePageName, utf7); break; } case CP_UTF8: { static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0}; cpinfo->CodePage = CP_UTF8; cpinfo->UnicodeDefaultChar = 0x3f; strcpyW(cpinfo->CodePageName, utf8); break; } case CP_UTF16LE: { cpinfo->CodePage = CP_UTF16LE; cpinfo->UnicodeDefaultChar = 0x3f; wcscpy(cpinfo->CodePageName, L"Unicode (UTF-16 LE)"); break; } case CP_UTF16BE: { cpinfo->CodePage = CP_UTF16BE; cpinfo->UnicodeDefaultChar = 0x003f; wcscpy(cpinfo->CodePageName, L"Unicode (UTF-16 BE)"); break; } case CP_UTF32LE: { cpinfo->CodePage = CP_UTF32LE; cpinfo->UnicodeDefaultChar = 0x3f; wcscpy(cpinfo->CodePageName, L"Unicode (UTF-32 LE)"); break; } case CP_UTF32BE: { cpinfo->CodePage = CP_UTF32BE; cpinfo->UnicodeDefaultChar = 0x0000003f; wcscpy(cpinfo->CodePageName, L"Unicode (UTF-32 BE)"); break; } default: { const union cptable *table = get_codepage_table( codepage ); if (!table) return FALSE; cpinfo->CodePage = table->info.codepage; cpinfo->UnicodeDefaultChar = table->info.def_unicode_char; WINPORT(MultiByteToWideChar)(CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName)/sizeof(WCHAR)); break; } } return TRUE; } WINPORT_DECL(EnumSystemCodePages, BOOL, (CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags)) { const union cptable *table; WCHAR buffer[10], *p; int page, index = 0; for (;;) { if (!(table = wine_cp_enum_table( index++ ))) break; p = buffer + sizeof(buffer)/sizeof(WCHAR); *--p = 0; page = table->info.codepage; do { *--p = '0' + (page % 10); page /= 10; } while( page ); if (!lpfnCodePageEnum( p )) break; } return TRUE; } }