From 5e458f6f4eae1aae120b90ec9b9ceadc60356acb Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 24 Aug 2004 17:21:12 +0000 Subject: [PATCH] - Implementation of GetCPFileNameFromRegistry, GetNlsSectionName and IsValidCodePage. - Partial implementation of MultiByteToWideChar and WideCharToMultiByte. - Cleanup buffer allocation in some console functions. svn path=/trunk/; revision=10668 --- reactos/lib/kernel32/kernel32.def | 4 +- reactos/lib/kernel32/kernel32.edf | 6 +- reactos/lib/kernel32/makefile | 5 +- reactos/lib/kernel32/misc/console.c | 49 +- reactos/lib/kernel32/misc/dllmain.c | 13 +- reactos/lib/kernel32/misc/lang.c | 16 +- reactos/lib/kernel32/misc/mbchars.c | 305 ----------- reactos/lib/kernel32/misc/nls.c | 797 ++++++++++++++++++++++++++++ 8 files changed, 842 insertions(+), 353 deletions(-) delete mode 100644 reactos/lib/kernel32/misc/mbchars.c create mode 100755 reactos/lib/kernel32/misc/nls.c diff --git a/reactos/lib/kernel32/kernel32.def b/reactos/lib/kernel32/kernel32.def index bfaf164a177..2dbd0f57d4c 100644 --- a/reactos/lib/kernel32/kernel32.def +++ b/reactos/lib/kernel32/kernel32.def @@ -280,7 +280,7 @@ GetAtomNameW@12 GetBinaryType@8 GetBinaryTypeA@8 GetBinaryTypeW@8 -;GetCPFileNameFromRegistry +GetCPFileNameFromRegistry@12 GetCPInfo@8 GetCPInfoExA@12 GetCPInfoExW@12 @@ -409,7 +409,7 @@ GetNamedPipeHandleStateW@28 GetNamedPipeInfo@20 GetNativeSystemInfo@4 GetNextVDMCommand@4 -;GetNlsSectionName +GetNlsSectionName@24 GetNumaHighestNodeNumber@4 GetNumaNodeProcessorMask@8 ;GetNumaProcessorMap diff --git a/reactos/lib/kernel32/kernel32.edf b/reactos/lib/kernel32/kernel32.edf index a33bd3b25a8..a04690ec546 100644 --- a/reactos/lib/kernel32/kernel32.edf +++ b/reactos/lib/kernel32/kernel32.edf @@ -1,4 +1,4 @@ -; $Id: kernel32.edf,v 1.32 2004/05/13 20:42:28 navaraf Exp $ +; $Id: kernel32.edf,v 1.33 2004/08/24 17:21:10 navaraf Exp $ ; ; kernel32.edf ; @@ -284,7 +284,7 @@ GetAtomNameW=GetAtomNameW@12 GetBinaryType=GetBinaryTypeA@8 GetBinaryTypeA=GetBinaryTypeA@8 GetBinaryTypeW=GetBinaryTypeW@8 -;GetCPFileNameFromRegistry +GetCPFileNameFromRegistry=GetCPFileNameFromRegistry@12 GetCPInfo=GetCPInfo@8 GetCPInfoExA=GetCPInfoExA@12 GetCPInfoExW=GetCPInfoExW@12 @@ -414,7 +414,7 @@ GetNamedPipeHandleStateW=GetNamedPipeHandleStateW@28 GetNamedPipeInfo=GetNamedPipeInfo@20 GetNativeSystemInfo=GetNativeSystemInfo@4 GetNextVDMCommand=GetNextVDMCommand@4 -;GetNlsSectionName +GetNlsSectionName=GetNlsSectionName@24 GetNumaHighestNodeNumber=GetNumaHighestNodeNumber@4 GetNumaNodeProcessorMask=GetNumaNodeProcessorMask@8 ;GetNumaProcessorMap diff --git a/reactos/lib/kernel32/makefile b/reactos/lib/kernel32/makefile index 13febe8f680..e172e577046 100644 --- a/reactos/lib/kernel32/makefile +++ b/reactos/lib/kernel32/makefile @@ -1,4 +1,4 @@ -# $Id: makefile,v 1.83 2004/08/01 23:27:55 navaraf Exp $ +# $Id: makefile,v 1.84 2004/08/24 17:21:10 navaraf Exp $ PATH_TO_TOP = ../.. @@ -33,8 +33,9 @@ MISC_OBJECTS = misc/error.o misc/atom.o misc/handle.o misc/env.o \ misc/console.o misc/time.o misc/timerqueue.o misc/toolhelp.o \ misc/stubs.o misc/lang.o misc/ldr.o misc/res.o \ misc/sysinfo.o misc/profile.o \ - misc/mbchars.o misc/muldiv.o misc/computername.o \ + misc/muldiv.o misc/nls.o misc/computername.o \ misc/perfcnt.o misc/lzexpand_main.o misc/lcformat.o + FILE_OBJECTS = file/file.o file/curdir.o file/lfile.o file/dir.o \ file/iocompl.o file/volume.o file/deviceio.o file/dosdev.o \ diff --git a/reactos/lib/kernel32/misc/console.c b/reactos/lib/kernel32/misc/console.c index 240b00ae44c..573c4e0e170 100644 --- a/reactos/lib/kernel32/misc/console.c +++ b/reactos/lib/kernel32/misc/console.c @@ -1,4 +1,4 @@ -/* $Id: console.c,v 1.77 2004/08/22 20:52:28 navaraf Exp $ +/* $Id: console.c,v 1.78 2004/08/24 17:21:11 navaraf Exp $ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries @@ -1073,10 +1073,10 @@ WriteConsoleA(HANDLE hConsoleOutput, USHORT Size; ULONG MessageSize; - Request = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - sizeof(CSRSS_API_REQUEST) + - CSRSS_MAX_WRITE_CONSOLE_REQUEST); + Request = RtlAllocateHeap(GetProcessHeap(), 0, + sizeof(CSRSS_API_REQUEST) + + min(nNumberOfCharsToWrite, + CSRSS_MAX_WRITE_CONSOLE_REQUEST)); if (Request == NULL) { SetLastError(ERROR_OUTOFMEMORY); @@ -1140,9 +1140,8 @@ BOOL STDCALL ReadConsoleA(HANDLE hConsoleInput, NTSTATUS Status; ULONG CharsRead = 0; - Reply = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - sizeof(CSRSS_API_REPLY) + nNumberOfCharsToRead); + Reply = RtlAllocateHeap(GetProcessHeap(), 0, + sizeof(CSRSS_API_REPLY) + nNumberOfCharsToRead); if (Reply == NULL) { SetLastError(ERROR_OUTOFMEMORY); @@ -1946,9 +1945,9 @@ ReadConsoleOutputCharacterA( NTSTATUS Status; DWORD Size; - Reply = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - sizeof(CSRSS_API_REPLY) + CSRSS_MAX_READ_CONSOLE_OUTPUT_CHAR); + Reply = RtlAllocateHeap(GetProcessHeap(), 0, + sizeof(CSRSS_API_REPLY) + + min(nLength, CSRSS_MAX_READ_CONSOLE_OUTPUT_CHAR)); if (Reply == NULL) { SetLastError(ERROR_OUTOFMEMORY); @@ -2035,9 +2034,9 @@ ReadConsoleOutputAttribute( NTSTATUS Status; DWORD Size, i; - Reply = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - sizeof(CSRSS_API_REPLY) + CSRSS_MAX_READ_CONSOLE_OUTPUT_ATTRIB); + Reply = RtlAllocateHeap(GetProcessHeap(), 0, + sizeof(CSRSS_API_REPLY) + + min(nLength, CSRSS_MAX_READ_CONSOLE_OUTPUT_ATTRIB)); if (Reply == NULL) { SetLastError(ERROR_OUTOFMEMORY); @@ -2101,10 +2100,10 @@ WriteConsoleOutputCharacterA(HANDLE hConsoleOutput, CSRSS_API_REPLY Reply; NTSTATUS Status; WORD Size; - - Request = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - sizeof(CSRSS_API_REQUEST) + CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR); + + Request = RtlAllocateHeap(GetProcessHeap(), 0, + sizeof(CSRSS_API_REQUEST) + + min(nLength, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR)); if( !Request ) { SetLastError( ERROR_OUTOFMEMORY ); @@ -2156,9 +2155,9 @@ WriteConsoleOutputCharacterW(HANDLE hConsoleOutput, NTSTATUS Status; WORD Size; - Request = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - sizeof(CSRSS_API_REQUEST) + CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR); + Request = RtlAllocateHeap(GetProcessHeap(), 0, + sizeof(CSRSS_API_REQUEST) + + min(nLength, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR)); if( !Request ) { SetLastError( ERROR_OUTOFMEMORY ); @@ -2223,9 +2222,9 @@ WriteConsoleOutputAttribute( WORD Size; int c; - Request = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - sizeof(CSRSS_API_REQUEST) + CSRSS_MAX_WRITE_CONSOLE_OUTPUT_ATTRIB); + Request = RtlAllocateHeap(GetProcessHeap(), 0, + sizeof(CSRSS_API_REQUEST) + + min(nLength, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_ATTRIB)); if( !Request ) { SetLastError( ERROR_OUTOFMEMORY ); @@ -3136,7 +3135,7 @@ GetConsoleCP( VOID ) /*-------------------------------------------------------------- * SetConsoleCP * - * @unimplemented + * @implemented */ BOOL WINAPI diff --git a/reactos/lib/kernel32/misc/dllmain.c b/reactos/lib/kernel32/misc/dllmain.c index afc8aff6fa6..f8e9d4c588f 100644 --- a/reactos/lib/kernel32/misc/dllmain.c +++ b/reactos/lib/kernel32/misc/dllmain.c @@ -1,4 +1,4 @@ -/* $Id: dllmain.c,v 1.35 2004/06/26 20:07:40 gdalsnes Exp $ +/* $Id: dllmain.c,v 1.36 2004/08/24 17:21:11 navaraf Exp $ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries @@ -40,6 +40,9 @@ CRITICAL_SECTION ConsoleLock; extern BOOL WINAPI DefaultConsoleCtrlHandler(DWORD Event); extern BOOL FASTCALL PROFILE_Init(); +extern BOOL FASTCALL NlsInit(); +extern VOID FASTCALL NlsUninit(); + /* FUNCTIONS *****************************************************************/ static NTSTATUS @@ -153,6 +156,12 @@ DllMain(HANDLE hDll, return FALSE; } + /* Initialize the National Language Support routines */ + if (! NlsInit()) + { + return FALSE; + } + /* Initialize console ctrl handler */ RtlInitializeCriticalSection(&ConsoleLock); SetConsoleCtrlHandler(DefaultConsoleCtrlHandler, TRUE); @@ -168,6 +177,8 @@ DllMain(HANDLE hDll, { /* Insert more dll detach stuff here! */ + NlsUninit(); + /* Delete DLL critical section */ RtlDeleteCriticalSection (&ConsoleLock); RtlDeleteCriticalSection (&DllLock); diff --git a/reactos/lib/kernel32/misc/lang.c b/reactos/lib/kernel32/misc/lang.c index edd70e49f87..8ac5920ef9e 100644 --- a/reactos/lib/kernel32/misc/lang.c +++ b/reactos/lib/kernel32/misc/lang.c @@ -1,4 +1,4 @@ -/* $Id: lang.c,v 1.20 2004/06/26 20:10:50 gdalsnes Exp $ +/* $Id: lang.c,v 1.21 2004/08/24 17:21:11 navaraf Exp $ * * COPYRIGHT: See COPYING in the top level directory * PROJECT : ReactOS user mode libraries @@ -907,20 +907,6 @@ GetUserGeoID( } -/* - * @unimplemented - */ -BOOL -STDCALL -IsValidCodePage ( - UINT CodePage - ) -{ - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - - /* * @unimplemented */ diff --git a/reactos/lib/kernel32/misc/mbchars.c b/reactos/lib/kernel32/misc/mbchars.c deleted file mode 100644 index 415abad9acf..00000000000 --- a/reactos/lib/kernel32/misc/mbchars.c +++ /dev/null @@ -1,305 +0,0 @@ -/* $Id: mbchars.c,v 1.5 2004/04/04 17:59:23 gvg Exp $ - * - */ -#include - - -/********************************************************************** - * NAME PRIVATE - * IsInstalledCP@4 - * - * RETURN VALUE - * TRUE if CodePage is installed in the system. - */ -static -BOOL -STDCALL -IsInstalledCP(UINT CodePage) -{ - /* FIXME */ - return TRUE; -} - - -/********************************************************************** - * NAME EXPORTED - * MultiByteToWideChar@24 - * - * ARGUMENTS - * CodePage - * CP_ACP ANSI code page - * CP_MACCP Macintosh code page - * CP_OEMCP OEM code page - * (UINT) Any installed code page - * - * dwFlags - * MB_PRECOMPOSED - * MB_COMPOSITE - * MB_ERR_INVALID_CHARS - * MB_USEGLYPHCHARS - * - * lpMultiByteStr - * Input buffer; - * - * cchMultiByte - * Size of MultiByteStr, or -1 if MultiByteStr is - * NULL terminated; - * - * lpWideCharStr - * Output buffer; - * - * cchWideChar - * Size (in WCHAR unit) of WideCharStr, or 0 - * if the caller just wants to know how large - * WideCharStr should be for a successful - * conversion. - * - * RETURN VALUE - * 0 on error; otherwise the number of WCHAR written - * in the WideCharStr buffer. - * - * NOTE - * A raw converter for now. It assumes lpMultiByteStr is - * NEVER multi-byte (that is each input character is - * 8-bit ASCII) and is ALWAYS NULL terminated. - * FIXME-FIXME-FIXME-FIXME - * - * @implemented - */ -INT -STDCALL -MultiByteToWideChar( - UINT CodePage, - DWORD dwFlags, - LPCSTR lpMultiByteStr, - int cchMultiByte, - LPWSTR lpWideCharStr, - int cchWideChar) -{ - int InStringLength = 0; - PCHAR r; - PWCHAR w; - int cchConverted; - - /* - * Check the parameters. - */ - if (/* --- CODE PAGE --- */ - ( (CP_ACP != CodePage) - && (CP_MACCP != CodePage) - && (CP_OEMCP != CodePage) - && (FALSE == IsInstalledCP(CodePage)) ) - /* --- FLAGS --- */ - || (dwFlags & ~(MB_PRECOMPOSED | MB_COMPOSITE | - MB_ERR_INVALID_CHARS | MB_USEGLYPHCHARS)) - /* --- INPUT BUFFER --- */ - || (NULL == lpMultiByteStr) ) - { - SetLastError (ERROR_INVALID_PARAMETER); - return 0; - } - /* - * Compute the input buffer length. - */ - //if (-1 == cchMultiByte) - if (cchMultiByte < 0) - { - InStringLength = lstrlenA(lpMultiByteStr) + 1; - } - else - { - InStringLength = cchMultiByte; - } - /* - * Does caller query for output - * buffer size? - */ - if (0 == cchWideChar) - { - //SetLastError(ERROR_SUCCESS); /* according to wine tests this shouldn't be touched on success. - return InStringLength; - } - /* - * Is space provided for the translated - * string enough? - */ - if (cchWideChar < InStringLength) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return 0; - } - /* - * Raw 8- to 16-bit conversion. - */ - for (cchConverted = 0, - r = (PCHAR)lpMultiByteStr, - w = (PWCHAR)lpWideCharStr; - - cchConverted < InStringLength; - - r++, - w++, - cchConverted++) - { - *w = (WCHAR)(unsigned char) *r; - } - /* - * Return how many characters we - * wrote in the output buffer. - */ - //SetLastError(ERROR_SUCCESS); /* according to wine tests this shouldn't be touched on success. - return cchConverted; -} - - -/********************************************************************** - * NAME EXPORTED - * WideCharToMultiByte@32 - * - * Not yet implemented complete (without NLS so far) - * - * ARGUMENTS - * CodePage - * CP_ACP ANSI code page - * CP_MACCP Macintosh code page - * CP_OEMCP OEM code page - * CP_SYMBOL Symbol code page (42) - * CP_THREAD_ACP Current thread's ANSI code page - * CP_UTF7 Translate using UTF-7 - * CP_UTF8 Translate using UTF-8 - * (UINT) Any installed code page - * - * dwFlags - * WC_NO_BEST_FIT_CHARS - * WC_COMPOSITECHECK Convert composite characters to precomposed characters. - * WC_DISCARDNS Discard nonspacing characters during conversion. - * WC_SEPCHARS Generate separate characters during conversion. This is the default conversion behavior. - * WC_DEFAULTCHAR Replace exceptions with the default character during conversion. - * - * lpWideCharStr - * Points to the wide-character string to be converted. - * - * cchWideChar - * Size (in WCHAR unit) of WideCharStr, or 0 - * if the caller just wants to know how large - * WideCharStr should be for a successful - * conversion. - * lpMultiByteStr - * Points to the buffer to receive the translated string. - * cchMultiByte - * Specifies the size in bytes of the buffer pointed to by the - * lpMultiByteStr parameter. If this value is zero, the function - * returns the number of bytes required for the buffer. - * lpDefaultChar - * 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. - FIXME: ignored - * lpUsedDefaultChar - * Points to a flag that indicates whether a default character was used. - * This parameter may be NULL. - FIXME: allways set to FALSE. - * - * - * - * RETURN VALUE - * 0 on error; otherwise the number of bytes written - * in the lpMultiByteStr buffer. Or the number of - * bytes needed for the lpMultiByteStr buffer if cchMultiByte is zero. - * - * NOTE - * A raw converter for now. It just cuts off the upper 9 Bit. - * So the MBCS-string does not contain any LeadCharacters - * FIXME - FIXME - FIXME - FIXME - * - * @implemented - */ -int -STDCALL -WideCharToMultiByte( - UINT CodePage, - DWORD dwFlags, - LPCWSTR lpWideCharStr, - int cchWideChar, - LPSTR lpMultiByteStr, - int cchMultiByte, - LPCSTR lpDefaultChar, - LPBOOL lpUsedDefaultChar - ) -{ - int wi, di; // wide counter, dbcs byte count - - /* - * Check the parameters. - */ - if ( /* --- CODE PAGE --- */ - ( (CP_ACP != CodePage) - && (CP_MACCP != CodePage) - && (CP_OEMCP != CodePage) - && (CP_SYMBOL != CodePage) - && (CP_THREAD_ACP != CodePage) - && (CP_UTF7 != CodePage) - && (CP_UTF8 != CodePage) - && (FALSE == IsInstalledCP (CodePage)) - ) - /* --- FLAGS --- */ - || (dwFlags & ~(/*WC_NO_BEST_FIT_CHARS - |*/ WC_COMPOSITECHECK - | WC_DISCARDNS - | WC_SEPCHARS - | WC_DEFAULTCHAR - ) - ) - /* --- INPUT BUFFER --- */ - || (NULL == lpWideCharStr) - ) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - // for now, make no difference but only convert cut the characters to 7Bit - //if (cchWideChar == -1) // assume its a 0-terminated str - if (cchWideChar < 0) // assume its a 0-terminated str - { // and determine its length -// for (cchWideChar=0; lpWideCharStr[cchWideChar]!=0; cchWideChar++) -// cchWideChar++; - for (cchWideChar = 0; lpWideCharStr[cchWideChar] != 0; cchWideChar++) { - } - cchWideChar++; // length includes the null terminator - } - - // user wants to determine needed space - if (cchMultiByte == 0) - { - //SetLastError(ERROR_SUCCESS); /* according to wine tests this shouldn't be touched on success. - return cchWideChar; // FIXME: determine correct. - } - // the lpWideCharStr is cchWideChar characters long. - for (wi=0, di=0; wi127) ) - { - lpMultiByteStr[di]= - *lpUsedDefaultChar = TRUE; - - }*/ - // FIXME - // just cut off the upper 9 Bit, since vals>=128 mean LeadByte. - lpMultiByteStr[di] = lpWideCharStr[wi] & 0x007F; - } - // has MultiByte exceeded but Wide is still in the string? - if (wi < cchWideChar && di >= cchMultiByte) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return 0; - } - // else return # of bytes wirtten to MBCSbuffer (di) - //SetLastError(ERROR_SUCCESS); /* according to wine tests this shouldn't be touched on success. - // FIXME: move that elsewhere - if (lpUsedDefaultChar != NULL) *lpUsedDefaultChar = FALSE; - return di; -} - -/* EOF */ diff --git a/reactos/lib/kernel32/misc/nls.c b/reactos/lib/kernel32/misc/nls.c new file mode 100755 index 00000000000..5adca77d914 --- /dev/null +++ b/reactos/lib/kernel32/misc/nls.c @@ -0,0 +1,797 @@ +/* + * ReactOS Kernel + * Copyright (C) 2004 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/** + * @brief National Language Support. + * @author Filip Navara + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include "../include/debug.h" + +/* GLOBAL VARIABLES ***********************************************************/ + +typedef struct _CODEPAGE_ENTRY +{ + LIST_ENTRY Entry; + UINT CodePage; + HANDLE SectionHandle; + PBYTE SectionMapping; + CPTABLEINFO CodePageTable; +} CODEPAGE_ENTRY, *PCODEPAGE_ENTRY; + +/* 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 CRITICAL_SECTION CodePageListLock; + +/* FORWARD DECLARATIONS *******************************************************/ + +BOOL STDCALL +GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown, + LPSTR BaseName, LPSTR Result, ULONG ResultSize); + +BOOL STDCALL +GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize); + +/* PRIVATE FUNCTIONS **********************************************************/ + +/** + * Internal NLS related stuff initialization. + */ +BOOL FASTCALL +NlsInit() +{ + 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); + NtCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes); + NtClose(Handle); + + /* Setup ANSI code page. */ + AnsiCodePage.CodePage = CP_ACP; + AnsiCodePage.SectionHandle = NULL; + AnsiCodePage.SectionMapping = NtCurrentTeb()->Peb->AnsiCodePageData; + RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping, + &AnsiCodePage.CodePageTable); + InsertTailList(&CodePageListHead, &AnsiCodePage.Entry); + + /* Setup OEM code page. */ + OemCodePage.CodePage = CP_OEMCP; + OemCodePage.SectionHandle = NULL; + OemCodePage.SectionMapping = NtCurrentTeb()->Peb->OemCodePageData; + RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping, + &OemCodePage.CodePageTable); + InsertTailList(&CodePageListHead, &OemCodePage.Entry); + + return TRUE; +} + +/** + * Internal NLS related stuff uninitialization. + */ +VOID FASTCALL +NlsUninit() +{ + 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); +} + +/** + * 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; +} + +/** + * 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_THREAD_ACP) + { + if (!GetLocaleInfoW(GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE | + LOCALE_RETURN_NUMBER, (WCHAR *)&CodePage, + sizeof(CodePage) / sizeof(WCHAR))) + { + /* Last error is set by GetLocaleInfoW. */ + return 0; + } + } + 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 0; + } + } + + /* 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_FILE, FileHandle); + } + } + 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; +} + +/** + * Internal version of MultiByteToWideChar for UTF8. + * + * @see MultiByteToWideChar + * @todo Add UTF8 validity checks. + */ +INT STDCALL +IntMultiByteToWideCharUTF8(DWORD Flags, + LPCSTR MultiByteString, INT MultiByteCount, + LPWSTR WideCharString, INT WideCharCount) +{ + LPCSTR MbsEnd; + UCHAR Char, Length; + WCHAR WideChar; + ULONG Count; + + if (Flags != 0) + { + 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 = UTF8Mask[Length]; + while (Length && MultiByteString < MbsEnd) + WideChar = (WideChar << 6) | *MultiByteString++; + *WideCharString++ = WideChar; + } + + if (MultiByteString < MbsEnd) + SetLastError(ERROR_INSUFFICIENT_BUFFER); + + return Count; +} + +/** + * Internal version of MultiByteToWideChar for code page tables. + * + * @see MultiByteToWideChar + * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and + * DBCS codepages. + */ +INT STDCALL +IntMultiByteToWideCharCP(UINT CodePage, DWORD Flags, + LPCSTR MultiByteString, INT MultiByteCount, + LPWSTR WideCharString, INT WideCharCount) +{ + PCODEPAGE_ENTRY CodePageEntry; + PCPTABLEINFO CodePageTable; + LPCSTR TempString; + 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->MaximumCharacterSize > 1) + { + /* UNIMPLEMENTED */ + return 0; + } + else + { + /* Check for invalid characters. */ + if (Flags & MB_ERR_INVALID_CHARS) + { + for (TempString = MultiByteString, TempLength = MultiByteCount; + TempLength > 0; + TempString++, TempLength--) + { + if (CodePageTable->MultiByteTable[(UCHAR)*TempString] == + CodePageTable->UniDefaultChar && + *TempString != CodePageEntry->CodePageTable.DefaultChar) + { + SetLastError(ERROR_NO_UNICODE_TRANSLATION); + return 0; + } + } + } + + /* Does caller query for output buffer size? */ + if (WideCharCount == 0) + return MultiByteCount; + + /* Adjust buffer size. Wine trick ;-) */ + if (WideCharCount < MultiByteCount) + { + MultiByteCount = WideCharCount; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + } + + for (TempLength = MultiByteCount; + TempLength > 0; + MultiByteString++, TempLength--) + { + *WideCharString++ = CodePageTable->MultiByteTable[(UCHAR)*MultiByteString]; + } + + return MultiByteCount; + } +} + +/** + * Internal version of WideCharToMultiByte for UTF8. + * + * @see WideCharToMultiByte + */ +INT STDCALL +IntWideCharToMultiByteUTF8(UINT CodePage, DWORD Flags, + LPCWSTR WideCharString, INT WideCharCount, + LPSTR MultiByteString, INT MultiByteCount, + LPCSTR DefaultChar, LPBOOL UsedDefaultChar) +{ + DPRINT1("WideCharToMultiByte for CP_UTF8 is not implemented!\n"); + return 0; +} + +/** + * Internal version of WideCharToMultiByte for code page tables. + * + * @see WideCharToMultiByte + * @todo Handle default characters and flags. + */ +INT STDCALL +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->MaximumCharacterSize > 1) + { + DPRINT1("WideCharToMultiByte for DBCS codepages is not implemented!\n"); + return 0; + } + else + { + /* Does caller query for output buffer size? */ + if (MultiByteCount == 0) + return WideCharCount; + + /* Adjust buffer size. Wine trick ;-) */ + if (MultiByteCount < WideCharCount) + { + WideCharCount = MultiByteCount; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + } + + for (TempLength = WideCharCount; + TempLength > 0; + WideCharString++, TempLength--) + { + *MultiByteString++ = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString]; + } + + /* FIXME */ + if (UsedDefaultChar != NULL) + *UsedDefaultChar = FALSE; + + return WideCharCount; + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/** + * 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 STDCALL +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; +} + +/** + * 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 STDCALL +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; + + /* Convert the codepage number to string. */ + ValueName.Buffer = ValueNameBuffer; + ValueName.MaximumLength = sizeof(ValueNameBuffer); + if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage, 10, &ValueName))) + return FALSE; + + /* 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)) + { + RtlFreeUnicodeString(&ValueName); + return FALSE; + } + + /* Allocate buffer that will be used to query the value data. */ + KvpiSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + + (MAX_PATH * sizeof(WCHAR)); + Kvpi = HeapAlloc(GetProcessHeap(), 0, KvpiSize); + if (Kvpi == NULL) + { + NtClose(KeyHandle); + RtlFreeUnicodeString(&ValueName); + return FALSE; + } + + /* Query the file name for our code page. */ + Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, + Kvpi, KvpiSize, &KvpiSize); + RtlFreeUnicodeString(&ValueName); + 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)) + { + if (FileName != NULL) + { + lstrcpynW(FileName, (WCHAR*)Kvpi->Data, + min(Kvpi->DataLength / sizeof(WCHAR), FileNameSize)); + } + return TRUE; + } + + return FALSE; +} + +/** + * 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 STDCALL +IsValidCodePage(UINT CodePage) +{ + if (CodePage == CP_UTF8 || CodePage == CP_UTF7) + return TRUE; + if (IntGetLoadedCodePageEntry(CodePage)) + return TRUE; + return GetCPFileNameFromRegistry(CodePage, NULL, 0); +} + +/** + * 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 STDCALL +MultiByteToWideChar(UINT CodePage, DWORD Flags, + LPCSTR MultiByteString, INT MultiByteCount, + LPWSTR WideCharString, INT WideCharCount) +{ + /* Check the parameters. */ + if (MultiByteString == NULL || + (WideCharString == NULL && WideCharCount > 0)) + { + 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: + DPRINT1("MultiByteToWideChar for CP_UTF7 is not implemented!\n"); + return 0; + + case CP_SYMBOL: + DPRINT1("MultiByteToWideChar for CP_SYMBOL is not implemented!\n"); + return 0; + + default: + return IntMultiByteToWideCharCP( + CodePage, Flags, MultiByteString, MultiByteCount, + WideCharString, WideCharCount); + } +} + +/** + * 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 STDCALL +WideCharToMultiByte(UINT CodePage, DWORD Flags, + LPCWSTR WideCharString, INT WideCharCount, + LPSTR MultiByteString, INT MultiByteCount, + LPCSTR DefaultChar, LPBOOL UsedDefaultChar) +{ + /* Check the parameters. */ + if (WideCharString == NULL || + (MultiByteString == NULL && 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: + return IntWideCharToMultiByteUTF8( + CodePage, Flags, WideCharString, WideCharCount, + MultiByteString, MultiByteCount, DefaultChar, + UsedDefaultChar); + + case CP_UTF7: + DPRINT1("WideCharToMultiByte for CP_UTF7 is not implemented!\n"); + return 0; + + case CP_SYMBOL: + DPRINT1("WideCharToMultiByte for CP_SYMBOL is not implemented!\n"); + return 0; + + default: + return IntWideCharToMultiByteCP( + CodePage, Flags, WideCharString, WideCharCount, + MultiByteString, MultiByteCount, DefaultChar, + UsedDefaultChar); + } +} + +/* EOF */