diff --git a/codecnv.c b/codecnv.c index 19c4d6b..5701a60 100644 --- a/codecnv.c +++ b/codecnv.c @@ -1469,6 +1469,9 @@ static int CheckOnEUC(uchar *Pos, uchar *Btm) // UTF-8対応 ここから↓ + +#define UNICODE_REPLACEMENT_CHARACTER (0xfffd) + /*----- UTF-8漢字コードをSHIFT-JIS漢字コードに変換 ------------------------------ * * Parameter @@ -1496,6 +1499,95 @@ int ConvUTF8NtoSJIS(CODECONVINFO *cInfo) int UTF16Length; int Count; + int UTF16BufSize; + int OrigSrcLength; + + Continue = NO; + + // 前回の変換不能な残りの文字列を入力の先頭に結合 + SrcLength = cInfo->StrLen + cInfo->EscUTF8Len; + if(!(pSrc = (char*)malloc(sizeof(char) * SrcLength))) + { + *(cInfo->Buf) = '\0'; + cInfo->BufSize = 0; + return Continue; + } + memcpy(pSrc, cInfo->EscUTF8, sizeof(char) * cInfo->EscUTF8Len); + memcpy(pSrc + cInfo->EscUTF8Len, cInfo->Str, sizeof(char) * cInfo->StrLen); + + // MultiByteToWideChar()の不完全文字に対する動作は準拠するUnicode規格の違いから、Windowsバージョンによって異なる。 + // (.NET Framework 2.0はMS07-040適用でUnicode5.0準拠となる。このときCRTがKB940521の影響を受けるかは未確認) + // + // 【Windows XP以前の動作】 + // UTF-8の場合、不完全な文字は常に変換されない + // バイナリ UTF-8 バイナリ UTF-16 LE + // E3 81 82 E3 81 84 あい -> 42 30 44 30 あい + // E3 81 82 E3 81 あ+E3 81 -> 42 30 あ + // E3 81 82 E3 あ+E3 -> 42 30 あ + // + // 【Windows Vista以降での動作】 + // Unicode4.1に準拠し、不完全な文字はU+FFFD(REPLACEMENT CHARACTER)に置き換えられる + // バイナリ UTF-8 バイナリ UTF-16 + // E3 81 82 E3 81 84 あい -> 42 30 44 30 あい (U+3042,U+3044) + // E3 81 82 E3 81 あ+E3 81 -> 42 30 FD FF あ??? (U+3042,U+FFFD) + // E3 81 82 E3 あ+E3 -> 42 30 FD FF あ??? (U+3042,U+FFFD) + + UTF16BufSize = MultiByteToWideChar(CP_UTF8, 0, pSrc, SrcLength, NULL, 0); + if(UTF16BufSize < 1 || !(pUTF16 = (wchar_t*)malloc(sizeof(wchar_t) * UTF16BufSize))) + { + free(pSrc); + *(cInfo->Buf) = '\0'; + cInfo->BufSize = 0; + return Continue; + } + + OrigSrcLength = SrcLength; + UTF16Length = UTF16BufSize; + // 無限ループを避けるため、変換処理は1バイト以上で処理 + while(SrcLength > 0) { + UTF16Length = MultiByteToWideChar(CP_UTF8, 0, pSrc, SrcLength, pUTF16, UTF16BufSize); + if (UTF16Length > 1 && *(pUTF16 + UTF16Length -1) == (wchar_t)UNICODE_REPLACEMENT_CHARACTER) { + Count = UTF16Length; + // UTF-16バッファ末尾のU+FFFDが削除されるようSrcLengthを調節。末尾以外はU+FFFDのままとする。UTF-16文字数をCount + do { + SrcLength--; + UTF16Length = MultiByteToWideChar(CP_UTF8, 0, pSrc, SrcLength, NULL, 0); + } while(SrcLength > 0 && Count == UTF16Length); + } + + + // UTF-16をASCIIに変換した場合の文字数を取得 + string_length = WideCharToMultiByte(CP_ACP, 0, pUTF16, UTF16Length, NULL, 0, NULL, NULL); + if(string_length > cInfo->BufSize) { + // バッファに収まらないため変換文字数を半減 + SrcLength /= 2; + Continue = YES; // 再呼び出しでcInfo->Strの処理が必要 + } + else { + break; + } + } + // バッファに収まる + cInfo->OutLen = WideCharToMultiByte(CP_ACP, 0, pUTF16, UTF16Length, cInfo->Buf, cInfo->BufSize, NULL, NULL); + if (Continue == YES) { + // 再呼び出し必要。処理されなかった元の部分文字列(バッファ不足のため)の先頭をStrバッファにコピー。Escバッファはクリア + cInfo->StrLen = OrigSrcLength - SrcLength; + cInfo->EscUTF8Len = 0; + memmove(cInfo->Str, pSrc + SrcLength, cInfo->StrLen); + } + else { + // 処理されなかった元の部分文字列(U+FFFDに変換された文字)をEscバッファにコピー。Strバッファはクリア。 + cInfo->StrLen = 0; + cInfo->EscUTF8Len = OrigSrcLength - SrcLength; + if (cInfo->EscUTF8Len > 0) { + memcpy(cInfo->EscUTF8, pSrc + SrcLength, cInfo->EscUTF8Len); + } + } + free(pSrc); + free(pUTF16); + + return(Continue); +#if 0 Continue = NO; // 生成される中間コードのサイズを調べる @@ -1518,6 +1610,7 @@ int ConvUTF8NtoSJIS(CODECONVINFO *cInfo) memcpy(pSrc, cInfo->EscUTF8, sizeof(char) * cInfo->EscUTF8Len); memcpy(pSrc + cInfo->EscUTF8Len, cInfo->Str, sizeof(char) * cInfo->StrLen); *(pSrc + SrcLength) = '\0'; + // UTF-8の場合、不完全な文字は常に変換されない UTF16Length = MultiByteToWideChar(CP_UTF8, 0, pSrc, SrcLength, NULL, 0); @@ -1609,11 +1702,11 @@ int ConvUTF8NtoSJIS(CODECONVINFO *cInfo) cInfo->StrLen = 0; Continue = NO; } - free(pSrc); free(pUTF16); return(Continue); +#endif } /*----- SHIFT-JIS漢字コードをUTF-8漢字コードに変換 ------------------------------