/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * FILE: dll/win32/kernel32/winnls/string/nls.c * PURPOSE: National Language Support * PROGRAMMER: Filip Navara * Hartmut Birr * Gunnar Andre Dalsnes * Thomas Weidenmueller * UPDATE HISTORY: * Created 24/08/2004 */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include /* GLOBAL VARIABLES ***********************************************************/ /* Sequence length based on the first character. */ static const char UTF8Length[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xC0 - 0xCF */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xD0 - 0xDF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xE0 - 0xEF */ 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 0, 0 /* 0xF0 - 0xFF */ }; /* First byte mask depending on UTF-8 sequence length. */ static const unsigned char UTF8Mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01}; /* FIXME: Change to HASH table or linear array. */ static LIST_ENTRY CodePageListHead; static CODEPAGE_ENTRY AnsiCodePage; static CODEPAGE_ENTRY OemCodePage; static RTL_CRITICAL_SECTION CodePageListLock; /* FORWARD DECLARATIONS *******************************************************/ BOOL WINAPI GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown, LPSTR BaseName, LPSTR Result, ULONG ResultSize); BOOL WINAPI GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize); /* PRIVATE FUNCTIONS **********************************************************/ /** * @name NlsInit * * Internal NLS related stuff initialization. */ BOOL FASTCALL NlsInit(VOID) { UNICODE_STRING DirName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Handle; InitializeListHead(&CodePageListHead); RtlInitializeCriticalSection(&CodePageListLock); /* * FIXME: Eventually this should be done only for the NLS Server * process, but since we don't have anything like that (yet?) we * always try to create the "\Nls" directory here. */ RtlInitUnicodeString(&DirName, L"\\Nls"); InitializeObjectAttributes(&ObjectAttributes, &DirName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL); if (NT_SUCCESS(NtCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes))) { NtClose(Handle); } /* Setup ANSI code page. */ AnsiCodePage.SectionHandle = NULL; AnsiCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->AnsiCodePageData; RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping, &AnsiCodePage.CodePageTable); AnsiCodePage.CodePage = AnsiCodePage.CodePageTable.CodePage; InsertTailList(&CodePageListHead, &AnsiCodePage.Entry); /* Setup OEM code page. */ OemCodePage.SectionHandle = NULL; OemCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->OemCodePageData; RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping, &OemCodePage.CodePageTable); OemCodePage.CodePage = OemCodePage.CodePageTable.CodePage; InsertTailList(&CodePageListHead, &OemCodePage.Entry); return TRUE; } /** * @name NlsUninit * * Internal NLS related stuff uninitialization. */ VOID FASTCALL NlsUninit(VOID) { PCODEPAGE_ENTRY Current; /* Delete the code page list. */ while (!IsListEmpty(&CodePageListHead)) { Current = CONTAINING_RECORD(CodePageListHead.Flink, CODEPAGE_ENTRY, Entry); if (Current->SectionHandle != NULL) { UnmapViewOfFile(Current->SectionMapping); NtClose(Current->SectionHandle); } RemoveHeadList(&CodePageListHead); } RtlDeleteCriticalSection(&CodePageListLock); } /** * @name IntGetLoadedCodePageEntry * * Internal function to get structure containing a code page information * of code page that is already loaded. * * @param CodePage * Number of the code page. Special values like CP_OEMCP, CP_ACP * or CP_UTF8 aren't allowed. * * @return Code page entry or NULL if the specified code page hasn't * been loaded yet. */ PCODEPAGE_ENTRY FASTCALL IntGetLoadedCodePageEntry(UINT CodePage) { LIST_ENTRY *CurrentEntry; PCODEPAGE_ENTRY Current; RtlEnterCriticalSection(&CodePageListLock); for (CurrentEntry = CodePageListHead.Flink; CurrentEntry != &CodePageListHead; CurrentEntry = CurrentEntry->Flink) { Current = CONTAINING_RECORD(CurrentEntry, CODEPAGE_ENTRY, Entry); if (Current->CodePage == CodePage) { RtlLeaveCriticalSection(&CodePageListLock); return Current; } } RtlLeaveCriticalSection(&CodePageListLock); return NULL; } /** * @name IntGetCodePageEntry * * Internal function to get structure containing a code page information. * * @param CodePage * Number of the code page. Special values like CP_OEMCP, CP_ACP * or CP_THREAD_ACP are allowed, but CP_UTF[7/8] isn't. * * @return Code page entry. */ PCODEPAGE_ENTRY FASTCALL IntGetCodePageEntry(UINT CodePage) { CHAR SectionName[40]; NTSTATUS Status; HANDLE SectionHandle = INVALID_HANDLE_VALUE, FileHandle; PBYTE SectionMapping; OBJECT_ATTRIBUTES ObjectAttributes; ANSI_STRING AnsiName; UNICODE_STRING UnicodeName; WCHAR FileName[MAX_PATH + 1]; UINT FileNamePos; PCODEPAGE_ENTRY CodePageEntry; if (CodePage == CP_ACP) { return &AnsiCodePage; } else if (CodePage == CP_OEMCP) { return &OemCodePage; } else if (CodePage == CP_THREAD_ACP) { if (!GetLocaleInfoW(GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, (WCHAR *)&CodePage, sizeof(CodePage) / sizeof(WCHAR))) { /* Last error is set by GetLocaleInfoW. */ return NULL; } if (CodePage == 0) return &AnsiCodePage; } else if (CodePage == CP_MACCP) { if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER, (WCHAR *)&CodePage, sizeof(CodePage) / sizeof(WCHAR))) { /* Last error is set by GetLocaleInfoW. */ return NULL; } } /* Try searching for loaded page first. */ CodePageEntry = IntGetLoadedCodePageEntry(CodePage); if (CodePageEntry != NULL) { return CodePageEntry; } /* * Yes, we really want to lock here. Otherwise it can happen that * two parallel requests will try to get the entry for the same * code page and we would load it twice. */ RtlEnterCriticalSection(&CodePageListLock); /* Generate the section name. */ if (!GetNlsSectionName(CodePage, 10, 0, "\\Nls\\NlsSectionCP", SectionName, sizeof(SectionName))) { RtlLeaveCriticalSection(&CodePageListLock); return NULL; } RtlInitAnsiString(&AnsiName, SectionName); RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, TRUE); InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0, NULL, NULL); /* Try to open the section first */ Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes); /* If the section doesn't exist, try to create it. */ if (Status == STATUS_UNSUCCESSFUL || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND) { FileNamePos = GetSystemDirectoryW(FileName, MAX_PATH); if (GetCPFileNameFromRegistry(CodePage, FileName + FileNamePos + 1, MAX_PATH - FileNamePos - 1)) { FileName[FileNamePos] = L'\\'; FileName[MAX_PATH] = 0; FileHandle = CreateFileW(FileName, FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); Status = NtCreateSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes, NULL, PAGE_READONLY, SEC_COMMIT, FileHandle); /* HACK: Check if another process was faster * and already created this section. See bug 3626 for details */ if (Status == STATUS_OBJECT_NAME_COLLISION) { /* Close the file then */ NtClose(FileHandle); /* And open the section */ Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes); } } } RtlFreeUnicodeString(&UnicodeName); if (!NT_SUCCESS(Status)) { RtlLeaveCriticalSection(&CodePageListLock); return NULL; } SectionMapping = MapViewOfFile(SectionHandle, FILE_MAP_READ, 0, 0, 0); if (SectionMapping == NULL) { NtClose(SectionHandle); RtlLeaveCriticalSection(&CodePageListLock); return NULL; } CodePageEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY)); if (CodePageEntry == NULL) { NtClose(SectionHandle); RtlLeaveCriticalSection(&CodePageListLock); return NULL; } CodePageEntry->CodePage = CodePage; CodePageEntry->SectionHandle = SectionHandle; CodePageEntry->SectionMapping = SectionMapping; RtlInitCodePageTable((PUSHORT)SectionMapping, &CodePageEntry->CodePageTable); /* Insert the new entry to list and unlock. Uff. */ InsertTailList(&CodePageListHead, &CodePageEntry->Entry); RtlLeaveCriticalSection(&CodePageListLock); return CodePageEntry; } /** * @name IntMultiByteToWideCharUTF8 * * Internal version of MultiByteToWideChar for UTF8. * * @see MultiByteToWideChar * @todo Add UTF8 validity checks. */ static INT WINAPI IntMultiByteToWideCharUTF8(DWORD Flags, LPCSTR MultiByteString, INT MultiByteCount, LPWSTR WideCharString, INT WideCharCount) { LPCSTR MbsEnd; UCHAR Char, Length; WCHAR WideChar; LONG Count; if (Flags != 0 && Flags != MB_ERR_INVALID_CHARS) { SetLastError(ERROR_INVALID_FLAGS); return 0; } /* Does caller query for output buffer size? */ if (WideCharCount == 0) { MbsEnd = MultiByteString + MultiByteCount; for (; MultiByteString < MbsEnd; WideCharCount++) { Char = *MultiByteString++; if (Char < 0xC0) continue; MultiByteString += UTF8Length[Char - 0x80]; } return WideCharCount; } MbsEnd = MultiByteString + MultiByteCount; for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++) { Char = *MultiByteString++; if (Char < 0x80) { *WideCharString++ = Char; continue; } Length = UTF8Length[Char - 0x80]; WideChar = Char & UTF8Mask[Length]; while (Length && MultiByteString < MbsEnd) { WideChar = (WideChar << 6) | (*MultiByteString++ & 0x7f); Length--; } *WideCharString++ = WideChar; } if (MultiByteString < MbsEnd) SetLastError(ERROR_INSUFFICIENT_BUFFER); return Count; } /** * @name IntMultiByteToWideCharCP * * Internal version of MultiByteToWideChar for code page tables. * * @see MultiByteToWideChar * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and * DBCS codepages. */ static INT WINAPI IntMultiByteToWideCharCP(UINT CodePage, DWORD Flags, LPCSTR MultiByteString, INT MultiByteCount, LPWSTR WideCharString, INT WideCharCount) { PCODEPAGE_ENTRY CodePageEntry; PCPTABLEINFO CodePageTable; PUSHORT MultiByteTable; LPCSTR TempString; INT TempLength; USHORT WideChar; /* Get code page table. */ CodePageEntry = IntGetCodePageEntry(CodePage); if (CodePageEntry == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } CodePageTable = &CodePageEntry->CodePageTable; /* If MB_USEGLYPHCHARS flag present and glyph table present */ if ((Flags & MB_USEGLYPHCHARS) && CodePageTable->MultiByteTable[256]) { /* Use glyph table */ MultiByteTable = CodePageTable->MultiByteTable + 256 + 1; } else { MultiByteTable = CodePageTable->MultiByteTable; } /* Different handling for DBCS code pages. */ if (CodePageTable->DBCSCodePage) { UCHAR Char; USHORT DBCSOffset; LPCSTR MbsEnd = MultiByteString + MultiByteCount; INT Count; if (Flags & MB_ERR_INVALID_CHARS) { TempString = MultiByteString; while (TempString < MbsEnd) { DBCSOffset = CodePageTable->DBCSOffsets[(UCHAR)*TempString]; if (DBCSOffset) { /* If lead byte is presented, but behind it there is no symbol */ if (((TempString + 1) == MbsEnd) || (*(TempString + 1) == 0)) { SetLastError(ERROR_NO_UNICODE_TRANSLATION); return 0; } WideChar = CodePageTable->DBCSOffsets[DBCSOffset + *(TempString + 1)]; if (WideChar == CodePageTable->UniDefaultChar && MAKEWORD(*(TempString + 1), *TempString) != CodePageTable->TransUniDefaultChar) { SetLastError(ERROR_NO_UNICODE_TRANSLATION); return 0; } TempString++; } else { WideChar = MultiByteTable[(UCHAR)*TempString]; if ((WideChar == CodePageTable->UniDefaultChar && *TempString != CodePageTable->TransUniDefaultChar) || /* "Private Use" characters */ (WideChar >= 0xE000 && WideChar <= 0xF8FF)) { SetLastError(ERROR_NO_UNICODE_TRANSLATION); return 0; } } TempString++; } } /* Does caller query for output buffer size? */ if (WideCharCount == 0) { for (; MultiByteString < MbsEnd; WideCharCount++) { Char = *MultiByteString++; DBCSOffset = CodePageTable->DBCSOffsets[Char]; if (!DBCSOffset) continue; if (MultiByteString < MbsEnd) MultiByteString++; } return WideCharCount; } for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++) { Char = *MultiByteString++; DBCSOffset = CodePageTable->DBCSOffsets[Char]; if (!DBCSOffset) { *WideCharString++ = MultiByteTable[Char]; continue; } if (MultiByteString == MbsEnd) { *WideCharString++ = UNICODE_NULL; } else if (*MultiByteString == 0) { *WideCharString++ = UNICODE_NULL; MultiByteString++; } else { *WideCharString++ = CodePageTable->DBCSOffsets[DBCSOffset + (UCHAR)*MultiByteString++]; } } if (MultiByteString < MbsEnd) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } return Count; } else /* SBCS code page */ { /* Check for invalid characters. */ if (Flags & MB_ERR_INVALID_CHARS) { for (TempString = MultiByteString, TempLength = MultiByteCount; TempLength > 0; TempString++, TempLength--) { WideChar = MultiByteTable[(UCHAR)*TempString]; if ((WideChar == CodePageTable->UniDefaultChar && *TempString != CodePageTable->TransUniDefaultChar) || /* "Private Use" characters */ (WideChar >= 0xE000 && WideChar <= 0xF8FF)) { SetLastError(ERROR_NO_UNICODE_TRANSLATION); return 0; } } } /* Does caller query for output buffer size? */ if (WideCharCount == 0) return MultiByteCount; /* Fill the WideCharString buffer with what will fit: Verified on WinXP */ for (TempLength = (WideCharCount < MultiByteCount) ? WideCharCount : MultiByteCount; TempLength > 0; MultiByteString++, TempLength--) { *WideCharString++ = MultiByteTable[(UCHAR)*MultiByteString]; } /* Adjust buffer size. Wine trick ;-) */ if (WideCharCount < MultiByteCount) { MultiByteCount = WideCharCount; SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } return MultiByteCount; } } /** * @name IntMultiByteToWideCharSYMBOL * * Internal version of MultiByteToWideChar for SYMBOL. * * @see MultiByteToWideChar */ static INT WINAPI IntMultiByteToWideCharSYMBOL(DWORD Flags, LPCSTR MultiByteString, INT MultiByteCount, LPWSTR WideCharString, INT WideCharCount) { LONG Count; UCHAR Char; INT WideCharMaxLen; if (Flags != 0) { SetLastError(ERROR_INVALID_FLAGS); return 0; } if (WideCharCount == 0) { return MultiByteCount; } WideCharMaxLen = WideCharCount > MultiByteCount ? MultiByteCount : WideCharCount; for (Count = 0; Count < WideCharMaxLen; Count++) { Char = MultiByteString[Count]; if ( Char < 0x20 ) { WideCharString[Count] = Char; } else { WideCharString[Count] = Char + 0xf000; } } if (MultiByteCount > WideCharMaxLen) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } return WideCharMaxLen; } /** * @name IntWideCharToMultiByteSYMBOL * * Internal version of WideCharToMultiByte for SYMBOL. * * @see WideCharToMultiByte */ static INT WINAPI IntWideCharToMultiByteSYMBOL(DWORD Flags, LPCWSTR WideCharString, INT WideCharCount, LPSTR MultiByteString, INT MultiByteCount) { LONG Count; INT MaxLen; WCHAR Char; if (Flags!=0) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } if (MultiByteCount == 0) { return WideCharCount; } MaxLen = MultiByteCount > WideCharCount ? WideCharCount : MultiByteCount; for (Count = 0; Count < MaxLen; Count++) { Char = WideCharString[Count]; if (Char < 0x20) { MultiByteString[Count] = (CHAR)Char; } else { if ((Char >= 0xf020) && (Char < 0xf100)) { MultiByteString[Count] = Char - 0xf000; } else { SetLastError(ERROR_NO_UNICODE_TRANSLATION); return 0; } } } if (WideCharCount > MaxLen) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } return MaxLen; } /** * @name IntWideCharToMultiByteUTF8 * * Internal version of WideCharToMultiByte for UTF8. * * @see WideCharToMultiByte */ static INT WINAPI IntWideCharToMultiByteUTF8(UINT CodePage, DWORD Flags, LPCWSTR WideCharString, INT WideCharCount, LPSTR MultiByteString, INT MultiByteCount, LPCSTR DefaultChar, LPBOOL UsedDefaultChar) { INT TempLength; DWORD Char; if (Flags) { SetLastError(ERROR_INVALID_FLAGS); return 0; } /* Does caller query for output buffer size? */ if (MultiByteCount == 0) { for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++) { TempLength++; if (*WideCharString >= 0x80) { TempLength++; if (*WideCharString >= 0x800) { TempLength++; if (*WideCharString >= 0xd800 && *WideCharString < 0xdc00 && WideCharCount >= 1 && WideCharString[1] >= 0xdc00 && WideCharString[1] <= 0xe000) { WideCharCount--; WideCharString++; TempLength++; } } } } return TempLength; } for (TempLength = MultiByteCount; WideCharCount; WideCharCount--, WideCharString++) { Char = *WideCharString; if (Char < 0x80) { if (!TempLength) { SetLastError(ERROR_INSUFFICIENT_BUFFER); break; } TempLength--; *MultiByteString++ = (CHAR)Char; continue; } if (Char < 0x800) /* 0x80-0x7ff: 2 bytes */ { if (TempLength < 2) { SetLastError(ERROR_INSUFFICIENT_BUFFER); break; } MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6; MultiByteString[0] = 0xc0 | Char; MultiByteString += 2; TempLength -= 2; continue; } /* surrogate pair 0x10000-0x10ffff: 4 bytes */ if (Char >= 0xd800 && Char < 0xdc00 && WideCharCount >= 1 && WideCharString[1] >= 0xdc00 && WideCharString[1] < 0xe000) { WideCharCount--; WideCharString++; if (TempLength < 4) { SetLastError(ERROR_INSUFFICIENT_BUFFER); break; } Char = (Char - 0xd800) << 10; Char |= *WideCharString - 0xdc00; ASSERT(Char <= 0xfffff); Char += 0x10000; ASSERT(Char <= 0x10ffff); MultiByteString[3] = 0x80 | (Char & 0x3f); Char >>= 6; MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6; MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6; MultiByteString[0] = 0xf0 | Char; MultiByteString += 4; TempLength -= 4; continue; } /* 0x800-0xffff: 3 bytes */ if (TempLength < 3) { SetLastError(ERROR_INSUFFICIENT_BUFFER); break; } MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6; MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6; MultiByteString[0] = 0xe0 | Char; MultiByteString += 3; TempLength -= 3; } return MultiByteCount - TempLength; } /** * @name IsValidSBCSMapping * * Checks if ch (single-byte character) is a valid mapping for wch * * @see IntWideCharToMultiByteCP */ static inline BOOL IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, UCHAR ch) { /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */ if (Flags & WC_NO_BEST_FIT_CHARS) return (CodePageTable->MultiByteTable[ch] == wch); /* By default, all characters except TransDefaultChar apply as a valid mapping for ch (so also "nearest" characters) */ if (ch != CodePageTable->TransDefaultChar) return TRUE; /* The only possible left valid mapping is the default character itself */ return (wch == CodePageTable->TransUniDefaultChar); } /** * @name IsValidDBCSMapping * * Checks if ch (double-byte character) is a valid mapping for wch * * @see IntWideCharToMultiByteCP */ static inline BOOL IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, USHORT ch) { /* If ch is the default character, but the wch is not, it can't be a valid mapping */ if (ch == CodePageTable->TransDefaultChar && wch != CodePageTable->TransUniDefaultChar) return FALSE; /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */ if (Flags & WC_NO_BEST_FIT_CHARS) { if(ch & 0xff00) { USHORT uOffset = CodePageTable->DBCSOffsets[ch >> 8]; /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */ return (CodePageTable->DBCSOffsets[uOffset + (ch & 0xff)] == wch); } return (CodePageTable->MultiByteTable[ch] == wch); } /* If we're still here, we have a valid mapping */ return TRUE; } /** * @name IntWideCharToMultiByteCP * * Internal version of WideCharToMultiByte for code page tables. * * @see WideCharToMultiByte * @todo Handle WC_COMPOSITECHECK */ static INT WINAPI IntWideCharToMultiByteCP(UINT CodePage, DWORD Flags, LPCWSTR WideCharString, INT WideCharCount, LPSTR MultiByteString, INT MultiByteCount, LPCSTR DefaultChar, LPBOOL UsedDefaultChar) { PCODEPAGE_ENTRY CodePageEntry; PCPTABLEINFO CodePageTable; INT TempLength; /* Get code page table. */ CodePageEntry = IntGetCodePageEntry(CodePage); if (CodePageEntry == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } CodePageTable = &CodePageEntry->CodePageTable; /* Different handling for DBCS code pages. */ if (CodePageTable->DBCSCodePage) { /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */ if (Flags || DefaultChar || UsedDefaultChar) { BOOL TempUsedDefaultChar; USHORT DefChar; /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have to check on every character */ if (!UsedDefaultChar) UsedDefaultChar = &TempUsedDefaultChar; *UsedDefaultChar = FALSE; /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */ if (DefaultChar) DefChar = DefaultChar[1] ? ((DefaultChar[0] << 8) | DefaultChar[1]) : DefaultChar[0]; else DefChar = CodePageTable->TransDefaultChar; /* Does caller query for output buffer size? */ if (!MultiByteCount) { for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++) { USHORT uChar; if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1) { /* FIXME: Handle WC_COMPOSITECHECK */ DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n"); } uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString]; /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */ if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar)) { uChar = DefChar; *UsedDefaultChar = TRUE; } /* Increment TempLength again if this is a double-byte character */ if (uChar & 0xff00) TempLength++; } return TempLength; } /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */ for (TempLength = MultiByteCount; WideCharCount && TempLength; TempLength--, WideCharString++, WideCharCount--) { USHORT uChar; if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1) { /* FIXME: Handle WC_COMPOSITECHECK */ DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n"); } uChar = ((PUSHORT)CodePageTable->WideCharTable)[*WideCharString]; /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */ if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar)) { uChar = DefChar; *UsedDefaultChar = TRUE; } /* Handle double-byte characters */ if (uChar & 0xff00) { /* Don't output a partial character */ if (TempLength == 1) break; TempLength--; *MultiByteString++ = uChar >> 8; } *MultiByteString++ = (char)uChar; } /* WideCharCount should be 0 if all characters were converted */ if (WideCharCount) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } return MultiByteCount - TempLength; } /* Does caller query for output buffer size? */ if (!MultiByteCount) { for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++) { /* Increment TempLength again if this is a double-byte character */ if (((PWCHAR)CodePageTable->WideCharTable)[*WideCharString] & 0xff00) TempLength++; } return TempLength; } /* Convert the WideCharString to the MultiByteString */ for (TempLength = MultiByteCount; WideCharCount && TempLength; TempLength--, WideCharString++, WideCharCount--) { USHORT uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString]; /* Is this a double-byte character? */ if (uChar & 0xff00) { /* Don't output a partial character */ if (TempLength == 1) break; TempLength--; *MultiByteString++ = uChar >> 8; } *MultiByteString++ = (char)uChar; } /* WideCharCount should be 0 if all characters were converted */ if (WideCharCount) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } return MultiByteCount - TempLength; } else /* SBCS code page */ { INT nReturn; /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */ if (Flags || DefaultChar || UsedDefaultChar) { BOOL TempUsedDefaultChar; CHAR DefChar; /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have to check on every character */ if (!UsedDefaultChar) UsedDefaultChar = &TempUsedDefaultChar; *UsedDefaultChar = FALSE; /* Does caller query for output buffer size? */ if (!MultiByteCount) { /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */ for (TempLength = 0; WideCharCount; TempLength++, WideCharString++, WideCharCount--) { if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1) { /* FIXME: Handle WC_COMPOSITECHECK */ DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n"); } if (!*UsedDefaultChar) *UsedDefaultChar = !IntIsValidSBCSMapping(CodePageTable, Flags, *WideCharString, ((PCHAR)CodePageTable->WideCharTable)[*WideCharString]); } return TempLength; } /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */ if (DefaultChar) DefChar = *DefaultChar; else DefChar = (CHAR)CodePageTable->TransDefaultChar; /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */ for (TempLength = MultiByteCount; WideCharCount && TempLength; MultiByteString++, TempLength--, WideCharString++, WideCharCount--) { if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1) { /* FIXME: Handle WC_COMPOSITECHECK */ DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n"); } *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString]; if (!IntIsValidSBCSMapping(CodePageTable, Flags, *WideCharString, *MultiByteString)) { *MultiByteString = DefChar; *UsedDefaultChar = TRUE; } } /* WideCharCount should be 0 if all characters were converted */ if (WideCharCount) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } return MultiByteCount - TempLength; } /* Does caller query for output buffer size? */ if (!MultiByteCount) return WideCharCount; /* Is the buffer large enough? */ if (MultiByteCount < WideCharCount) { /* Convert the string up to MultiByteCount and return 0 */ WideCharCount = MultiByteCount; SetLastError(ERROR_INSUFFICIENT_BUFFER); nReturn = 0; } else { /* Otherwise WideCharCount will be the number of converted characters */ nReturn = WideCharCount; } /* Convert the WideCharString to the MultiByteString */ for (TempLength = WideCharCount; --TempLength >= 0; WideCharString++, MultiByteString++) { *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString]; } return nReturn; } } /** * @name IntIsLeadByte * * Internal function to detect if byte is lead byte in specific character * table. */ static BOOL WINAPI IntIsLeadByte(PCPTABLEINFO TableInfo, BYTE Byte) { UINT i; if (TableInfo->MaximumCharacterSize == 2) { for (i = 0; i < MAXIMUM_LEADBYTES && TableInfo->LeadByte[i]; i += 2) { if (Byte >= TableInfo->LeadByte[i] && Byte <= TableInfo->LeadByte[i+1]) return TRUE; } } return FALSE; } /* PUBLIC FUNCTIONS ***********************************************************/ /** * @name GetNlsSectionName * * Construct a name of NLS section. * * @param CodePage * Code page number. * @param Base * Integer base used for converting to string. Usually set to 10. * @param Unknown * As the name suggests the meaning of this parameter is unknown. * The native version of Kernel32 passes it as the third parameter * to NlsConvertIntegerToString function, which is used for the * actual conversion of the code page number. * @param BaseName * Base name of the section. (ex. "\\Nls\\NlsSectionCP") * @param Result * Buffer that will hold the constructed name. * @param ResultSize * Size of the buffer for the result. * * @return TRUE if the buffer was large enough and was filled with * the requested information, FALSE otherwise. * * @implemented */ BOOL WINAPI GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown, LPSTR BaseName, LPSTR Result, ULONG ResultSize) { CHAR Integer[11]; if (!NT_SUCCESS(RtlIntegerToChar(CodePage, Base, sizeof(Integer), Integer))) return FALSE; /* * If the name including the terminating NULL character doesn't * fit in the output buffer then fail. */ if (strlen(Integer) + strlen(BaseName) >= ResultSize) return FALSE; lstrcpyA(Result, BaseName); lstrcatA(Result, Integer); return TRUE; } /** * @name GetCPFileNameFromRegistry * * Get file name of code page definition file. * * @param CodePage * Code page number to get file name of. * @param FileName * Buffer that is filled with file name of successful return. Can * be set to NULL. * @param FileNameSize * Size of the buffer to hold file name in WCHARs. * * @return TRUE if the file name was retrieved, FALSE otherwise. * * @implemented */ BOOL WINAPI GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize) { WCHAR ValueNameBuffer[11]; UNICODE_STRING KeyName, ValueName; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; HANDLE KeyHandle; PKEY_VALUE_PARTIAL_INFORMATION Kvpi; DWORD KvpiSize; BOOL bRetValue; bRetValue = FALSE; /* Convert the codepage number to string. */ ValueName.Buffer = ValueNameBuffer; ValueName.MaximumLength = sizeof(ValueNameBuffer); if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage, 10, &ValueName))) return bRetValue; /* Open the registry key containing file name mappings. */ RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\" L"CurrentControlSet\\Control\\Nls\\CodePage"); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { return bRetValue; } /* Allocate buffer that will be used to query the value data. */ KvpiSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + (MAX_PATH * sizeof(WCHAR)); Kvpi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KvpiSize); if (Kvpi == NULL) { NtClose(KeyHandle); return bRetValue; } /* Query the file name for our code page. */ Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, Kvpi, KvpiSize, &KvpiSize); NtClose(KeyHandle); /* Check if we succeded and the value is non-empty string. */ if (NT_SUCCESS(Status) && Kvpi->Type == REG_SZ && Kvpi->DataLength > sizeof(WCHAR)) { bRetValue = TRUE; if (FileName != NULL) { lstrcpynW(FileName, (WCHAR*)Kvpi->Data, min(Kvpi->DataLength / sizeof(WCHAR), FileNameSize)); } } /* free temporary buffer */ HeapFree(GetProcessHeap(),0,Kvpi); return bRetValue; } /** * @name IsValidCodePage * * Detect if specified code page is valid and present in the system. * * @param CodePage * Code page number to query. * * @return TRUE if code page is present. */ BOOL WINAPI IsValidCodePage(UINT CodePage) { if (CodePage == 0) return FALSE; if (CodePage == CP_UTF8 || CodePage == CP_UTF7) return TRUE; if (IntGetLoadedCodePageEntry(CodePage)) return TRUE; return GetCPFileNameFromRegistry(CodePage, NULL, 0); } 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; } static INT Utf7ToWideChar(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, '+')) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } 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)) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } 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)) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } src++; } } return dest_index; } /** * @name MultiByteToWideChar * * Convert a multi-byte string to wide-charater equivalent. * * @param CodePage * Code page to be used to perform the conversion. It can be also * one of the special values (CP_ACP for ANSI code page, CP_MACCP * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP * for thread active code page, CP_UTF7 or CP_UTF8). * @param Flags * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE, * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS). * @param MultiByteString * Input buffer. * @param MultiByteCount * Size of MultiByteString, or -1 if MultiByteString is NULL * terminated. * @param WideCharString * Output buffer. * @param WideCharCount * Size in WCHARs of WideCharString, or 0 if the caller just wants * to know how large WideCharString should be for a successful * conversion. * * @return Zero on error, otherwise the number of WCHARs written * in the WideCharString buffer. * * @implemented */ INT WINAPI MultiByteToWideChar(UINT CodePage, DWORD Flags, LPCSTR MultiByteString, INT MultiByteCount, LPWSTR WideCharString, INT WideCharCount) { /* Check the parameters. */ if (MultiByteString == NULL || MultiByteCount == 0 || WideCharCount < 0 || (WideCharCount && (WideCharString == NULL || (PVOID)MultiByteString == (PVOID)WideCharString))) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } /* Determine the input string length. */ if (MultiByteCount < 0) { MultiByteCount = lstrlenA(MultiByteString) + 1; } switch (CodePage) { case CP_UTF8: return IntMultiByteToWideCharUTF8(Flags, MultiByteString, MultiByteCount, WideCharString, WideCharCount); case CP_UTF7: if (Flags) { SetLastError(ERROR_INVALID_FLAGS); return 0; } return Utf7ToWideChar(MultiByteString, MultiByteCount, WideCharString, WideCharCount); case CP_SYMBOL: return IntMultiByteToWideCharSYMBOL(Flags, MultiByteString, MultiByteCount, WideCharString, WideCharCount); default: return IntMultiByteToWideCharCP(CodePage, Flags, MultiByteString, MultiByteCount, WideCharString, WideCharCount); } } 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; } 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; } static INT WideCharToUtf7(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, '+')) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } if (!utf7_write_c(dst, dstlen, &dest_index, '-')) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } src++; } else if (utf7_can_directly_encode(*src)) { if (!utf7_write_c(dst, dstlen, &dest_index, *src)) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } src++; } else { unsigned int offset = 0; DWORD byte_pair = 0; if (!utf7_write_c(dst, dstlen, &dest_index, '+')) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } 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])) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } 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) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } byte_pair <<= (6 - offset); if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F])) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } } /* 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, '-')) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } } } return dest_index; } /* * A function similar to LoadStringW, but adapted for usage by GetCPInfoExW * and GetGeoInfoW. It uses the current user localization, otherwise falls back * to English (US). Contrary to LoadStringW which always saves the loaded string * into the user-given buffer, truncating the string if needed, this function * returns instead an ERROR_INSUFFICIENT_BUFFER error code if the user buffer * is not large enough. */ UINT GetLocalisedText( IN UINT uID, IN LPWSTR lpszDest, IN UINT cchDest) { HRSRC hrsrc; HGLOBAL hmem; LCID lcid; LANGID langId; const WCHAR *p; UINT i; /* See HACK in winnls/lang/xx-XX.rc files */ if (uID == 37) uID = uID * 100; lcid = GetUserDefaultLCID(); lcid = ConvertDefaultLocale(lcid); langId = LANGIDFROMLCID(lcid); if (PRIMARYLANGID(langId) == LANG_NEUTRAL) langId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); hrsrc = FindResourceExW(hCurrentModule, (LPWSTR)RT_STRING, MAKEINTRESOURCEW((uID >> 4) + 1), langId); /* English fallback */ if (!hrsrc) { hrsrc = FindResourceExW(hCurrentModule, (LPWSTR)RT_STRING, MAKEINTRESOURCEW((uID >> 4) + 1), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); } if (!hrsrc) goto NotFound; hmem = LoadResource(hCurrentModule, hrsrc); if (!hmem) goto NotFound; p = LockResource(hmem); for (i = 0; i < (uID & 0x0F); i++) p += *p + 1; /* Needed for GetGeoInfo(): return the needed string size including the NULL terminator */ if (cchDest == 0) return *p + 1; /* Needed for GetGeoInfo(): bail out if the user buffer is not large enough */ if (*p + 1 > cchDest) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } i = *p; if (i > 0) { memcpy(lpszDest, p + 1, i * sizeof(WCHAR)); lpszDest[i] = L'\0'; return i; } #if 0 else { if (cchDest >= 1) lpszDest[0] = L'\0'; /* Fall-back */ } #endif NotFound: DPRINT1("Resource not found: uID = %lu\n", uID); SetLastError(ERROR_INVALID_PARAMETER); return 0; } /* * @implemented */ BOOL WINAPI GetCPInfo(UINT CodePage, LPCPINFO CodePageInfo) { PCODEPAGE_ENTRY CodePageEntry; if (!CodePageInfo) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } CodePageEntry = IntGetCodePageEntry(CodePage); if (CodePageEntry == NULL) { switch(CodePage) { case CP_UTF7: case CP_UTF8: CodePageInfo->DefaultChar[0] = 0x3f; CodePageInfo->DefaultChar[1] = 0; CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0; CodePageInfo->MaxCharSize = (CodePage == CP_UTF7) ? 5 : 4; return TRUE; } DPRINT1("Invalid CP!: %lx\n", CodePage); SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } if (CodePageEntry->CodePageTable.DefaultChar & 0xff00) { CodePageInfo->DefaultChar[0] = (CodePageEntry->CodePageTable.DefaultChar & 0xff00) >> 8; CodePageInfo->DefaultChar[1] = CodePageEntry->CodePageTable.DefaultChar & 0x00ff; } else { CodePageInfo->DefaultChar[0] = CodePageEntry->CodePageTable.DefaultChar & 0xff; CodePageInfo->DefaultChar[1] = 0; } if ((CodePageInfo->MaxCharSize = CodePageEntry->CodePageTable.MaximumCharacterSize) == 2) memcpy(CodePageInfo->LeadByte, CodePageEntry->CodePageTable.LeadByte, sizeof(CodePageInfo->LeadByte)); else CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0; return TRUE; } /* * @implemented */ BOOL WINAPI GetCPInfoExW(UINT CodePage, DWORD dwFlags, LPCPINFOEXW lpCPInfoEx) { if (!GetCPInfo(CodePage, (LPCPINFO)lpCPInfoEx)) return FALSE; switch(CodePage) { case CP_UTF7: { lpCPInfoEx->CodePage = CP_UTF7; lpCPInfoEx->UnicodeDefaultChar = 0x3f; return GetLocalisedText(lpCPInfoEx->CodePage, lpCPInfoEx->CodePageName, ARRAYSIZE(lpCPInfoEx->CodePageName)) != 0; } break; case CP_UTF8: { lpCPInfoEx->CodePage = CP_UTF8; lpCPInfoEx->UnicodeDefaultChar = 0x3f; return GetLocalisedText(lpCPInfoEx->CodePage, lpCPInfoEx->CodePageName, ARRAYSIZE(lpCPInfoEx->CodePageName)) != 0; } default: { PCODEPAGE_ENTRY CodePageEntry; CodePageEntry = IntGetCodePageEntry(CodePage); if (CodePageEntry == NULL) { DPRINT1("Could not get CodePage Entry! CodePageEntry = NULL\n"); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } lpCPInfoEx->CodePage = CodePageEntry->CodePageTable.CodePage; lpCPInfoEx->UnicodeDefaultChar = CodePageEntry->CodePageTable.UniDefaultChar; return GetLocalisedText(lpCPInfoEx->CodePage, lpCPInfoEx->CodePageName, ARRAYSIZE(lpCPInfoEx->CodePageName)) != 0; } break; } } /* * @implemented */ BOOL WINAPI GetCPInfoExA(UINT CodePage, DWORD dwFlags, LPCPINFOEXA lpCPInfoEx) { CPINFOEXW CPInfo; if (!GetCPInfoExW(CodePage, dwFlags, &CPInfo)) return FALSE; /* the layout is the same except for CodePageName */ memcpy(lpCPInfoEx, &CPInfo, sizeof(CPINFOEXA)); WideCharToMultiByte(CP_ACP, 0, CPInfo.CodePageName, -1, lpCPInfoEx->CodePageName, sizeof(lpCPInfoEx->CodePageName), NULL, NULL); return TRUE; } /** * @name WideCharToMultiByte * * Convert a wide-charater string to closest multi-byte equivalent. * * @param CodePage * Code page to be used to perform the conversion. It can be also * one of the special values (CP_ACP for ANSI code page, CP_MACCP * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP * for thread active code page, CP_UTF7 or CP_UTF8). * @param Flags * Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK, * WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR). * @param WideCharString * Points to the wide-character string to be converted. * @param WideCharCount * Size in WCHARs of WideCharStr, or 0 if the caller just wants to * know how large WideCharString should be for a successful conversion. * @param MultiByteString * Points to the buffer to receive the translated string. * @param MultiByteCount * Specifies the size in bytes of the buffer pointed to by the * MultiByteString parameter. If this value is zero, the function * returns the number of bytes required for the buffer. * @param DefaultChar * Points to the character used if a wide character cannot be * represented in the specified code page. If this parameter is * NULL, a system default value is used. * @param UsedDefaultChar * Points to a flag that indicates whether a default character was * used. This parameter can be NULL. * * @return Zero on error, otherwise the number of bytes written in the * MultiByteString buffer. Or the number of bytes needed for * the MultiByteString buffer if MultiByteCount is zero. * * @implemented */ INT WINAPI WideCharToMultiByte(UINT CodePage, DWORD Flags, LPCWSTR WideCharString, INT WideCharCount, LPSTR MultiByteString, INT MultiByteCount, LPCSTR DefaultChar, LPBOOL UsedDefaultChar) { /* Check the parameters. */ if (WideCharString == NULL || WideCharCount == 0 || (MultiByteString == NULL && MultiByteCount > 0) || (PVOID)WideCharString == (PVOID)MultiByteString || MultiByteCount < 0) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } /* Determine the input string length. */ if (WideCharCount < 0) { WideCharCount = lstrlenW(WideCharString) + 1; } switch (CodePage) { case CP_UTF8: if (DefaultChar != NULL || UsedDefaultChar != NULL) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } return IntWideCharToMultiByteUTF8(CodePage, Flags, WideCharString, WideCharCount, MultiByteString, MultiByteCount, DefaultChar, UsedDefaultChar); case CP_UTF7: if (DefaultChar != NULL || UsedDefaultChar != NULL) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } if (Flags) { SetLastError(ERROR_INVALID_FLAGS); return 0; } return WideCharToUtf7(WideCharString, WideCharCount, MultiByteString, MultiByteCount); case CP_SYMBOL: if ((DefaultChar!=NULL) || (UsedDefaultChar!=NULL)) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } return IntWideCharToMultiByteSYMBOL(Flags, WideCharString, WideCharCount, MultiByteString, MultiByteCount); default: return IntWideCharToMultiByteCP(CodePage, Flags, WideCharString, WideCharCount, MultiByteString, MultiByteCount, DefaultChar, UsedDefaultChar); } } /** * @name GetACP * * Get active ANSI code page number. * * @implemented */ UINT WINAPI GetACP(VOID) { return AnsiCodePage.CodePageTable.CodePage; } /** * @name GetOEMCP * * Get active OEM code page number. * * @implemented */ UINT WINAPI GetOEMCP(VOID) { return OemCodePage.CodePageTable.CodePage; } /** * @name IsDBCSLeadByteEx * * Determine if passed byte is lead byte in specified code page. * * @implemented */ BOOL WINAPI IsDBCSLeadByteEx(UINT CodePage, BYTE TestByte) { PCODEPAGE_ENTRY CodePageEntry; CodePageEntry = IntGetCodePageEntry(CodePage); if (CodePageEntry != NULL) return IntIsLeadByte(&CodePageEntry->CodePageTable, TestByte); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } /** * @name IsDBCSLeadByteEx * * Determine if passed byte is lead byte in current ANSI code page. * * @implemented */ BOOL WINAPI IsDBCSLeadByte(BYTE TestByte) { return IntIsLeadByte(&AnsiCodePage.CodePageTable, TestByte); } /* * @unimplemented */ NTSTATUS WINAPI CreateNlsSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor,ULONG Size,ULONG AccessMask) { STUB; return 0; } /* * @unimplemented */ BOOL WINAPI IsValidUILanguage(LANGID langid) { STUB; return 0; } /* * @unimplemented */ VOID WINAPI NlsConvertIntegerToString(ULONG Value,ULONG Base,ULONG strsize, LPWSTR str, ULONG strsize2) { STUB; } /* * @unimplemented */ UINT WINAPI SetCPGlobal(UINT CodePage) { STUB; return 0; } /* * @unimplemented */ BOOL WINAPI ValidateLCType(int a1, unsigned int a2, int a3, int a4) { STUB; return FALSE; } /* * @unimplemented */ BOOL WINAPI NlsResetProcessLocale(VOID) { STUB; return TRUE; } /* * @unimplemented */ VOID WINAPI GetDefaultSortkeySize(LPVOID lpUnknown) { STUB; lpUnknown = NULL; } /* * @unimplemented */ VOID WINAPI GetLinguistLangSize(LPVOID lpUnknown) { STUB; lpUnknown = NULL; } /* * @unimplemented */ BOOL WINAPI ValidateLocale(IN ULONG LocaleId) { STUB; return TRUE; } /* * @unimplemented */ ULONG WINAPI NlsGetCacheUpdateCount(VOID) { STUB; return 0; } /* * @unimplemented */ BOOL WINAPI IsNLSDefinedString(IN NLS_FUNCTION Function, IN DWORD dwFlags, IN LPNLSVERSIONINFO lpVersionInformation, IN LPCWSTR lpString, IN INT cchStr) { STUB; return TRUE; } /* * @unimplemented */ BOOL WINAPI GetNLSVersion(IN NLS_FUNCTION Function, IN LCID Locale, IN OUT LPNLSVERSIONINFO lpVersionInformation) { STUB; return TRUE; } /* EOF */