#include #include #include "imeattr.h" #include "imedefs.h" #include "imerc.h" #include "uniime.h" BOOL IsBig5Character( WCHAR wChar ); /**********************************************************************/ /* AddPhraseString() */ /**********************************************************************/ DWORD PASCAL AddPhraseString( LPIMEL lpImeL, LPCANDIDATELIST lpCandList, DWORD dwPhraseOffset, DWORD dwPhraseNextOffset, DWORD dwStartLen, DWORD dwEndLen, DWORD dwAddCandLimit, BOOL fConvertCP) { HANDLE hLCPhraseTbl; LPWSTR lpLCPhraseTbl; LPWSTR lpStart, lpEnd; int iBytes; DWORD dwMaxCand; DWORD dwOffset; BOOL bIsNotBig5Char, bIsBig5OnlyMode; // put the strings into candidate list hLCPhraseTbl = OpenFileMapping(FILE_MAP_READ, FALSE, sImeG.szTblFile[1]); if (!hLCPhraseTbl) { return (0); } lpLCPhraseTbl = (LPWSTR)MapViewOfFile(hLCPhraseTbl, FILE_MAP_READ, 0, 0, 0); if (!lpLCPhraseTbl) { dwOffset = 0; goto AddPhraseStringUnmap; } if (lpImeL->fdwModeConfig & MODE_CONFIG_BIG5ONLY) bIsBig5OnlyMode = TRUE; else bIsBig5OnlyMode = FALSE; lpStart = lpLCPhraseTbl + dwPhraseOffset; lpEnd = lpLCPhraseTbl + dwPhraseNextOffset; if (!lpCandList) { // ask how many candidate list strings dwOffset = 0; for (lpStart; lpStart < lpEnd;) { bIsNotBig5Char = FALSE; for (; lpStart < lpEnd; lpStart++) { WORD uCode; uCode = *lpStart; // one string finished #ifdef UNICODE if (!uCode) { #else if (!(uCode & 0x8000)) { #endif lpStart++; break; } if ( bIsBig5OnlyMode ) { if ( IsBig5Character((WCHAR)uCode) == FALSE ) bIsNotBig5Char = TRUE; } } // if it is in Big5 Only Mode, and there is at least one char which is not // in Big5 charset, we don't count this string if ( bIsBig5OnlyMode && bIsNotBig5Char ) continue; // string count plus 1 dwOffset++; } goto AddPhraseStringUnmap; } // the offset of dwOffset[0] dwOffset = (DWORD)((LPBYTE)&lpCandList->dwOffset[0] - (LPBYTE)lpCandList); if (lpCandList->dwSize < dwOffset) { return (0); } // how many bytes of dwOffset[] iBytes = lpCandList->dwOffset[0] - dwOffset; // maybe the size is even smaller than it for (dwMaxCand = 1; dwMaxCand < lpCandList->dwCount; dwMaxCand++) { if ((int)(lpCandList->dwOffset[dwMaxCand] - dwOffset) < iBytes) { iBytes = (int)(lpCandList->dwOffset[dwMaxCand] - dwOffset); } } if (iBytes <= 0) { return (0); } dwMaxCand = (DWORD)iBytes / sizeof(DWORD); if (dwAddCandLimit < dwMaxCand) { dwMaxCand = dwAddCandLimit; } if (lpCandList->dwCount >= dwMaxCand) { // Grow memory here and do something, // if you still want to process it. return (0); } dwOffset = lpCandList->dwOffset[lpCandList->dwCount]; for (lpStart; lpStart < lpEnd;) { BOOL fStrEnd; DWORD dwStrLen, dwCharLen, dwStrByteLen, dwCharByteLen; fStrEnd = FALSE; bIsNotBig5Char = FALSE; // get the whole string dwCharByteLen = sizeof(WCHAR); dwCharLen = sizeof(WCHAR) / sizeof(TCHAR); for (dwStrLen = dwStrByteLen = 0; !fStrEnd && (lpStart < lpEnd); lpStart++, dwStrLen+= dwCharLen, dwStrByteLen += dwCharByteLen) { WORD uCode; uCode = *lpStart; // one string finished #ifdef UNICODE if (!uCode) { #else if (!(uCode & 0x8000)) { #endif fStrEnd = TRUE; #ifdef UNICODE lpStart++; break; #else uCode |= 0x8000; #endif } // if it is Big5Only Mode, we need to check if this char is in Big5 charset if ( bIsBig5OnlyMode ) { if ( !IsBig5Character((WCHAR)uCode) ) bIsNotBig5Char = TRUE; } #ifdef UNICODE if (fConvertCP) { CHAR szCode[4]; dwCharLen = dwCharByteLen = WideCharToMultiByte( sImeG.uAnsiCodePage, WC_COMPOSITECHECK, (LPCWSTR)&uCode, 1, szCode, sizeof(szCode), NULL, NULL); // because this BIG5 code, convert to BIG5 string if (dwCharByteLen >= 2) { uCode = (BYTE)szCode[0] | ((UINT)(BYTE)szCode[1] << 8); } else { uCode = (UINT)szCode[0]; } } #else // swap lead & second byte (as a string), UNICODE don't need it uCode = HIBYTE(uCode) | (LOBYTE(uCode) << 8); #endif if ((dwOffset + dwStrByteLen + dwCharByteLen) >= lpCandList->dwSize) { goto AddPhraseStringClose; } // add this char into candidate list #ifdef UNICODE if (dwCharByteLen == sizeof(WCHAR)) { *(LPWSTR)((LPBYTE)lpCandList + dwOffset + dwStrByteLen) = (WCHAR)uCode; } else { *(LPSTR)((LPBYTE)lpCandList + dwOffset + dwStrByteLen) = (CHAR)uCode; } #else *(LPWSTR)((LPBYTE)lpCandList + dwOffset + dwStrByteLen) = (WCHAR)uCode; #endif } if (dwStrLen < dwStartLen) { // the found string too short continue; } else if (dwStrLen >= dwEndLen) { // the found string too long continue; } else { } // if it is in Big5 Only Mode, and there is at least one char which is not in Big5 // charset, we just ingore this string, do not put it into the candidate list if ( bIsBig5OnlyMode && bIsNotBig5Char ) { bIsNotBig5Char = FALSE; continue; } if ((dwOffset + dwStrByteLen + sizeof(TCHAR)) >= lpCandList->dwSize) { goto AddPhraseStringClose; } // null terminator *(LPTSTR)((LPBYTE)lpCandList + dwOffset + dwStrByteLen) = TEXT('\0'); dwOffset += (dwStrByteLen + sizeof(TCHAR)); // add one string into candidate list lpCandList->dwCount++; if (lpCandList->dwCount >= dwMaxCand) { // Grow memory here and do something, // if you still want to process it. break; } // string length plus size of the null terminator lpCandList->dwOffset[lpCandList->dwCount] = dwOffset; } AddPhraseStringUnmap: UnmapViewOfFile(lpLCPhraseTbl); AddPhraseStringClose: CloseHandle(hLCPhraseTbl); return (dwOffset); } /**********************************************************************/ /* UniSearchPhrasePrediction() */ /* Description: */ /* file format can be changed in different version for */ /* performance consideration, ISVs should not assume its format */ /* and serach these files by themselves */ /**********************************************************************/ DWORD WINAPI UniSearchPhrasePrediction( LPIMEL lpImeL, UINT uCodePage, LPCTSTR lpszStr, DWORD dwStrLen, LPCTSTR lpszReadStr, // Phonetic reading string DWORD dwReadStrLen, DWORD dwStartLen, // find the string length >= this value DWORD dwEndLen, // find the string length < this value DWORD dwAddCandLimit, LPCANDIDATELIST lpCandList) { UINT uCode; HANDLE hLCPtrTbl; LPWORD lpLCPtrTbl; int iLo, iHi, iMid; BOOL fFound, fConvertCP; DWORD dwPhraseOffset, dwPhraseNextOffset; if (uCodePage == NATIVE_CP) { fConvertCP = FALSE; #ifdef UNICODE } else if (uCodePage == sImeG.uAnsiCodePage) { fConvertCP = TRUE; #endif } else { return (0); } if (dwStrLen != sizeof(WCHAR) / sizeof(TCHAR)) { return (0); } if (dwStartLen >= dwEndLen) { return (0); } #ifdef UNICODE uCode = lpszStr[0]; #else // swap lead byte & second byte, UNICODE don't need it uCode = (BYTE)lpszStr[1]; *((LPBYTE)&uCode + 1) = (BYTE)lpszStr[0]; #endif iLo = 0; #ifdef UNICODE iHi = sImeG.uTblSize[0] / 6; #else iHi = sImeG.uTblSize[0] / 4; #endif iMid = (iHi + iLo) /2; fFound = FALSE; // LCPTR.TBL hLCPtrTbl = OpenFileMapping(FILE_MAP_READ, FALSE, sImeG.szTblFile[0]); if (!hLCPtrTbl) { return (0); } lpLCPtrTbl = MapViewOfFile(hLCPtrTbl, FILE_MAP_READ, 0, 0, 0); if (!lpLCPtrTbl) { goto SrchPhrPredictClose; } // binary search on phrase table, // one is multiple word phrase and the other is the two word phrase for (; iLo <= iHi;) { LPWORD lpCurr; #ifdef UNICODE lpCurr = lpLCPtrTbl + 3 * iMid; #else lpCurr = lpLCPtrTbl + 2 * iMid; #endif if (uCode > *lpCurr) { iLo = iMid + 1; } else if (uCode < *lpCurr) { iHi = iMid - 1; } else { fFound = TRUE; // use it on TAB key #ifdef UNICODE dwPhraseOffset = *(LPUNADWORD)(lpCurr + 1); dwPhraseNextOffset = *(LPUNADWORD)(lpCurr + 1 + 3); #else dwPhraseOffset = *(lpCurr + 1); dwPhraseNextOffset = *(lpCurr + 1 + 2); #endif break; } iMid = (iHi + iLo) /2; } UnmapViewOfFile(lpLCPtrTbl); SrchPhrPredictClose: CloseHandle(hLCPtrTbl); if (!fFound) { return (0); } // phrase string return AddPhraseString(lpImeL,lpCandList, dwPhraseOffset, dwPhraseNextOffset, dwStartLen, dwEndLen, dwAddCandLimit, fConvertCP); } /**********************************************************************/ /* UniSearchPhrasePredictionStub() */ /* Description: */ /* file format can be changed in different version for */ /* performance consideration, ISVs should not assume its format */ /* and serach these files by themselves */ /**********************************************************************/ DWORD WINAPI UniSearchPhrasePredictionStub( LPIMEL lpImeL, UINT uCodePage, LPCSTUBSTR lpszStr, DWORD dwStrLen, LPCSTUBSTR lpszReadStr, // Phonetic reading string DWORD dwReadStrLen, DWORD dwStartLen, // find the string length >= this value DWORD dwEndLen, // find the string length < this value DWORD dwAddCandLimit, LPCANDIDATELIST lpCandList) { #ifdef UNICODE LPTSTR lpszWideStr, lpszWideReadStr; DWORD dwWideStrLen, dwWideReadStrLen; DWORD dwWideStartLen, dwWideEndLen; DWORD dwWideAddCandList, dwRet; LPCANDIDATELIST lpWideCandList; LPBYTE lpbBuf; if (uCodePage != sImeG.uAnsiCodePage) { return (0); } dwRet = dwStrLen * sizeof(WCHAR) + dwReadStrLen * sizeof(WCHAR); lpbBuf = (LPBYTE)GlobalAlloc(GPTR, dwRet); if ( lpbBuf == NULL ) return 0; if (lpszStr) { lpszWideStr = (LPTSTR)lpbBuf; dwWideStrLen = MultiByteToWideChar(sImeG.uAnsiCodePage, MB_PRECOMPOSED, lpszStr, dwStrLen, lpszWideStr, dwStrLen); } else { lpszWideStr = NULL; dwWideStrLen = 0; } if (lpszReadStr) { lpszWideReadStr = (LPTSTR)(lpbBuf + dwStrLen * sizeof(WCHAR)); dwWideReadStrLen = MultiByteToWideChar(sImeG.uAnsiCodePage, MB_PRECOMPOSED, lpszReadStr, dwReadStrLen, lpszWideReadStr, dwReadStrLen); } else { lpszWideReadStr = NULL; dwWideReadStrLen = 0; } dwRet = UniSearchPhrasePrediction(lpImeL,uCodePage, lpszWideStr, dwWideStrLen, lpszWideReadStr, dwWideReadStrLen, dwStartLen, dwEndLen, dwAddCandLimit, lpCandList); // now, start W to A conversion and fliter the real limit here GlobalFree((HGLOBAL)lpbBuf); return (dwRet); #else return (0); #endif } /**********************************************************************/ /* MemoryLack() */ /**********************************************************************/ void PASCAL MemoryLack( DWORD fdwErrMsg) { TCHAR szErrMsg[64]; TCHAR szIMEName[16]; if (sImeG.fdwErrMsg & fdwErrMsg) { // message already prompted return; } LoadString(hInst, IDS_MEM_LACK_FAIL, szErrMsg, sizeof(szErrMsg)/sizeof(TCHAR)); LoadString(hInst, IDS_IMENAME, szIMEName, sizeof(szIMEName)/sizeof(TCHAR) ); sImeG.fdwErrMsg |= fdwErrMsg; MessageBeep((UINT)-1); MessageBox((HWND)NULL, szErrMsg, szIMEName, MB_OK|MB_ICONHAND|MB_TASKMODAL|MB_TOPMOST); return; } /**********************************************************************/ /* LoadOneGlobalTable() */ /* Description: */ /* memory handle & size of .TBL file will be assigned to */ /* sImeG */ /* Eeturn Value: */ /* length of directory of the .TBL file */ /**********************************************************************/ UINT PASCAL LoadOneGlobalTable( // load one of table file LPTSTR szTable, // file name of .TBL UINT uIndex, // the index of array to store memory handle UINT uLen, // length of the directory LPTSTR szPath) // buffer for directory { HANDLE hTblFile; HGLOBAL hMap; TCHAR szFullPathFile[MAX_PATH]; PSECURITY_ATTRIBUTES psa; CopyMemory(szFullPathFile, szPath, uLen * sizeof(TCHAR)); psa = CreateSecurityAttributes(); if (uLen) { CopyMemory(&szFullPathFile[uLen], szTable, sizeof(sImeG.szTblFile[0])); hTblFile = CreateFile(szFullPathFile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, psa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); } else { // try system directory uLen = GetSystemDirectory(szFullPathFile, MAX_PATH); if (szFullPathFile[uLen - 1] != TEXT('\\')) { // consider N:\ ; szFullPathFile[uLen++] = TEXT('\\'); } CopyMemory(&szFullPathFile[uLen], szTable, sizeof(sImeG.szTblFile[0])); hTblFile = CreateFile(szFullPathFile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, psa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hTblFile != INVALID_HANDLE_VALUE) { goto CopyDicPath; } // if the work station version, SHARE_WRITE will fail hTblFile = CreateFile(szFullPathFile, GENERIC_READ, FILE_SHARE_READ, psa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); CopyDicPath: if (hTblFile != INVALID_HANDLE_VALUE) { CopyMemory(sImeG.szPhrasePath, szFullPathFile, uLen * sizeof(TCHAR)); sImeG.uPathLen = uLen; goto OpenDicFile; } } OpenDicFile: // can not find the table file if (hTblFile != INVALID_HANDLE_VALUE) { // OK } else if (sImeG.fdwErrMsg & (ERRMSG_LOAD_0 << uIndex)) { // already prompt error message before, no more FreeSecurityAttributes(psa); return (0); } else { // prompt error message TCHAR szIMEName[64]; TCHAR szErrMsg[2 * MAX_PATH]; // temp use szIMEName as format string buffer of error message LoadString(hInst, IDS_FILE_OPEN_FAIL, szIMEName, sizeof(szIMEName)/sizeof(TCHAR)); wsprintf(szErrMsg, szIMEName, szTable); LoadString(hInst, IDS_IMENAME, szIMEName, sizeof(szIMEName)/sizeof(TCHAR)); sImeG.fdwErrMsg |= ERRMSG_LOAD_0 << uIndex; MessageBeep((UINT)-1); MessageBox((HWND)NULL, szErrMsg, szIMEName, MB_OK|MB_ICONHAND|MB_TASKMODAL|MB_TOPMOST); FreeSecurityAttributes(psa); return (0); } sImeG.fdwErrMsg &= ~(ERRMSG_LOAD_0 << uIndex); // create file mapping for IME tables hMap = CreateFileMapping((HANDLE)hTblFile, psa, PAGE_READONLY, 0, 0, szTable); if (!hMap) { MemoryLack(ERRMSG_MEM_0 << uIndex); CloseHandle(hTblFile); FreeSecurityAttributes(psa); return(0); } sImeG.fdwErrMsg &= ~(ERRMSG_MEM_0 << uIndex); sInstG.hMapTbl[uIndex] = hMap; // get file length sImeG.uTblSize[uIndex] = GetFileSize(hTblFile, (LPDWORD)NULL); CloseHandle(hTblFile); FreeSecurityAttributes(psa); return (uLen); } /**********************************************************************/ /* LoadPhraseTable() */ /* Return Value: */ /* TRUE - successful, FALSE - failure */ /**********************************************************************/ BOOL PASCAL LoadPhraseTable( // load the phrase tables UINT uLen, // length of the directory LPTSTR szPath) // buffer for directory { int i; for (i = 0; i < MAX_PHRASE_TABLES; i++) { if (!*sImeG.szTblFile[i]) { } else if (sInstG.hMapTbl[i]) { // already loaded } else if (uLen = LoadOneGlobalTable(sImeG.szTblFile[i], i, uLen, szPath)) { } else { int j; for (j = 0; j < i; j++) { if (sInstG.hMapTbl[j]) { CloseHandle(sInstG.hMapTbl[j]); sInstG.hMapTbl[j] = (HANDLE)NULL; } } return (FALSE); } } return (TRUE); }