From 7ac7a902c87d7d82e6b2431b8a44260eb5149ce5 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 19 Nov 2019 10:08:27 +0100 Subject: [PATCH] ntdll: Implement RtlLocaleNameToLcid(). Signed-off-by: Alexandre Julliard --- dlls/kernel32/tests/locale.c | 79 +++++++++++++++++-- dlls/ntdll/loader.c | 2 + dlls/ntdll/locale.c | 142 +++++++++++++++++++++++++++++++++++ dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/ntdll_misc.h | 1 + include/winternl.h | 1 + 6 files changed, 221 insertions(+), 5 deletions(-) diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 75134fc30a4..b87f99c1c2a 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -30,11 +30,14 @@ #include #include +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "wine/test.h" #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winnls.h" +#include "winternl.h" static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0}; static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0}; @@ -83,6 +86,7 @@ static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROCA, DWORD, LONG_PTR); static BOOL (WINAPI *pEnumSystemLocalesEx)(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID); static INT (WINAPI *pLCMapStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPWSTR, INT, LPNLSVERSIONINFO, LPVOID, LPARAM); static LCID (WINAPI *pLocaleNameToLCID)(LPCWSTR, DWORD); +static NTSTATUS (WINAPI *pRtlLocaleNameToLcid)(LPCWSTR, LCID *, DWORD); static INT (WINAPI *pLCIDToLocaleName)(LCID, LPWSTR, INT, DWORD); static INT (WINAPI *pFoldStringA)(DWORD, LPCSTR, INT, LPSTR, INT); static INT (WINAPI *pFoldStringW)(DWORD, LPCWSTR, INT, LPWSTR, INT); @@ -148,6 +152,7 @@ static void InitFunctionPointers(void) mod = GetModuleHandleA("ntdll"); X(RtlUpcaseUnicodeChar); + X(RtlLocaleNameToLcid); #undef X } @@ -2792,9 +2797,12 @@ static const struct neutralsublang_name_t neutralsublang_names[] = { static void test_LocaleNameToLCID(void) { - LCID lcid; + LCID lcid, expect; + NTSTATUS status; INT ret; WCHAR buffer[LOCALE_NAME_MAX_LENGTH]; + const struct neutralsublang_name_t *ptr; + static const WCHAR enW[] = {'e','n',0}; static const WCHAR esesW[] = {'e','s','-','e','s',0}; static const WCHAR zhHansW[] = {'z','h','-','H','a','n','s',0}; @@ -2853,9 +2861,7 @@ static void test_LocaleNameToLCID(void) broken(lcid == 0) /* Vista */, "got 0x%04x\n", lcid); if (lcid) { - const struct neutralsublang_name_t *ptr = neutralsublang_names; - - while (*ptr->name) + for (ptr = neutralsublang_names; *ptr->name; ptr++) { lcid = pLocaleNameToLCID(ptr->name, 0); todo_wine_if (ptr->todo) @@ -2868,7 +2874,6 @@ static void test_LocaleNameToLCID(void) ok(!lstrcmpW(ptr->sname, buffer), "%s: got wrong locale name %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(buffer)); - ptr++; } /* zh-Hant has LCID 0x7c04, but LocaleNameToLCID actually returns 0x0c04, which is the LCID of zh-HK */ @@ -2919,6 +2924,70 @@ static void test_LocaleNameToLCID(void) ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n", wine_dbgstr_w(zhhansW), wine_dbgstr_w(buffer)); } + + if (pRtlLocaleNameToLcid) + { + status = pRtlLocaleNameToLcid( LOCALE_NAME_USER_DEFAULT, &lcid, 0 ); + ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status ); + status = pRtlLocaleNameToLcid( LOCALE_NAME_SYSTEM_DEFAULT, &lcid, 0 ); + ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status ); + status = pRtlLocaleNameToLcid( invalidW, &lcid, 0 ); + ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status ); + + lcid = 0; + status = pRtlLocaleNameToLcid( LOCALE_NAME_INVARIANT, &lcid, 0 ); + ok( !status, "failed error %x\n", status ); + ok( lcid == LANG_INVARIANT, "got %08x\n", lcid ); + + lcid = 0; + status = pRtlLocaleNameToLcid( localeW, &lcid, 0 ); + ok( !status, "failed error %x\n", status ); + ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), "got %08x\n", lcid ); + + lcid = 0; + status = pRtlLocaleNameToLcid( esesW, &lcid, 0 ); + ok( !status, "failed error %x\n", status ); + ok( lcid == MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), "got %08x\n", lcid ); + + lcid = 0; + status = pRtlLocaleNameToLcid( enW, &lcid, 0 ); + ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status ); + status = pRtlLocaleNameToLcid( enW, &lcid, 1 ); + ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status ); + status = pRtlLocaleNameToLcid( enW, &lcid, 2 ); + ok( !status, "failed error %x\n", status ); + ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), "got %08x\n", lcid ); + status = pRtlLocaleNameToLcid( L"en-RR", &lcid, 2 ); + ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status ); + status = pRtlLocaleNameToLcid( L"en-Latn-RR", &lcid, 2 ); + ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status ); + + for (ptr = neutralsublang_names; *ptr->name; ptr++) + { + switch (LANGIDFROMLCID(ptr->lcid)) + { + case MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN): expect = LANG_SERBIAN_NEUTRAL; break; + case MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_CYRILLIC): expect = 0x6c1a; break; + case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ): expect = 0x7804; break; + case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ): expect = LANG_CHINESE_TRADITIONAL; break; + default: expect = MAKELANGID( PRIMARYLANGID(ptr->lcid), SUBLANG_NEUTRAL ); break; + } + + status = pRtlLocaleNameToLcid( ptr->name, &lcid, 2 ); + ok( !status || broken(ptr->lcid == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)), /* vista */ + "%s failed error %x\n", wine_dbgstr_w(ptr->name), status ); + todo_wine_if(ptr->todo) + if (!status) ok( lcid == expect, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", + wine_dbgstr_w(ptr->name), lcid, expect ); + status = pRtlLocaleNameToLcid( ptr->sname, &lcid, 0 ); + ok( !status || broken(ptr->lcid == MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN)), /* vista */ + "%s failed error %x\n", wine_dbgstr_w(ptr->name), status ); + todo_wine_if(ptr->todo) + if (!status) ok( lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", + wine_dbgstr_w(ptr->name), lcid, ptr->lcid ); + } + } + else win_skip( "RtlLocaleNameToLcid not available\n" ); } /* this requires collation table patch to make it MS compatible */ diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index f8e1d0f589b..d87d6857d72 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -4281,6 +4281,8 @@ void __wine_process_init(void) exit(1); } + init_locale( wm->ldr.BaseAddress ); + params = peb->ProcessParameters; if (!(status = load_dll( params->DllPath.Buffer, params->ImagePathName.Buffer, NULL, DONT_RESOLVE_DLL_REFERENCES, &wm ))) diff --git a/dlls/ntdll/locale.c b/dlls/ntdll/locale.c index 71c4391ea72..73f72b19866 100644 --- a/dlls/ntdll/locale.c +++ b/dlls/ntdll/locale.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define NONAMELESSUNION #include "config.h" #include "wine/port.h" @@ -37,8 +38,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(nls); LCID user_lcid = 0, system_lcid = 0; static LANGID user_ui_language, system_ui_language; +static HMODULE kernel32_handle; static const union cptable *unix_table; /* NULL if UTF8 */ +static NTSTATUS load_string( ULONG id, LANGID lang, WCHAR *buffer, ULONG len ) +{ + const IMAGE_RESOURCE_DATA_ENTRY *data; + LDR_RESOURCE_INFO info; + NTSTATUS status; + WCHAR *p; + int i; + + info.Type = 6; /* RT_STRING */ + info.Name = (id >> 4) + 1; + info.Language = lang; + if ((status = LdrFindResource_U( kernel32_handle, &info, 3, &data ))) return status; + p = (WCHAR *)((char *)kernel32_handle + data->OffsetToData); + for (i = 0; i < (id & 0x0f); i++) p += *p + 1; + if (*p >= len) return STATUS_BUFFER_TOO_SMALL; + memcpy( buffer, p + 1, *p * sizeof(WCHAR) ); + buffer[*p] = 0; + return STATUS_SUCCESS; +} + + #if !defined(__APPLE__) && !defined(__ANDROID__) /* these platforms always use UTF-8 */ /* charset to codepage map, sorted by name */ @@ -143,6 +166,12 @@ void init_unix_codepage(void) { } #endif /* __APPLE__ || __ANDROID__ */ +void init_locale( HMODULE module ) +{ + kernel32_handle = module; +} + + /****************************************************************** * ntdll_umbstowcs */ @@ -234,3 +263,116 @@ NTSTATUS WINAPI NtQueryInstallUILanguage( LANGID *lang ) *lang = system_ui_language; return STATUS_SUCCESS; } + + +/****************************************************************** + * RtlLocaleNameToLcid (NTDLL.@) + */ +NTSTATUS WINAPI RtlLocaleNameToLcid( const WCHAR *name, LCID *lcid, ULONG flags ) +{ + /* locale name format is: lang[-script][-country][_modifier] */ + + static const WCHAR sepW[] = {'-','_',0}; + + const IMAGE_RESOURCE_DIRECTORY *resdir; + const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; + LDR_RESOURCE_INFO info; + WCHAR buf[LOCALE_NAME_MAX_LENGTH]; + WCHAR lang[LOCALE_NAME_MAX_LENGTH]; /* language ("en") (note: buffer contains the other strings too) */ + WCHAR *country = NULL; /* country ("US") */ + WCHAR *script = NULL; /* script ("Latn") */ + WCHAR *p; + int i; + + if (!name) return STATUS_INVALID_PARAMETER_1; + + if (!name[0]) + { + *lcid = LANG_INVARIANT; + goto found; + } + if (strlenW( name ) >= LOCALE_NAME_MAX_LENGTH) return STATUS_INVALID_PARAMETER_1; + strcpyW( lang, name ); + + if ((p = strpbrkW( lang, sepW )) && *p == '-') + { + *p++ = 0; + country = p; + if ((p = strpbrkW( p, sepW )) && *p == '-') + { + *p++ = 0; + script = country; + country = p; + p = strpbrkW( p, sepW ); + } + if (p) *p = 0; /* FIXME: modifier is ignored */ + /* second value can be script or country, check length to resolve the ambiguity */ + if (!script && strlenW( country ) == 4) + { + script = country; + country = NULL; + } + } + + info.Type = 6; /* RT_STRING */ + info.Name = (LOCALE_SNAME >> 4) + 1; + if (LdrFindResourceDirectory_U( kernel32_handle, &info, 2, &resdir )) + return STATUS_INVALID_PARAMETER_1; + + et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); + for (i = 0; i < resdir->NumberOfNamedEntries + resdir->NumberOfIdEntries; i++) + { + LANGID id = et[i].u.Id; + + if (PRIMARYLANGID(id) == LANG_NEUTRAL) continue; + + if (!load_string( LOCALE_SNAME, id, buf, ARRAY_SIZE(buf) ) && !strcmpiW( name, buf )) + { + *lcid = MAKELCID( id, SORT_DEFAULT ); /* FIXME: handle sort order */ + goto found; + } + + if (load_string( LOCALE_SISO639LANGNAME, id, buf, ARRAY_SIZE(buf) ) || strcmpiW( lang, buf )) + continue; + + if (script) + { + unsigned int len = strlenW( script ); + if (load_string( LOCALE_SSCRIPTS, id, buf, ARRAY_SIZE(buf) )) continue; + p = buf; + while (*p) + { + if (!strncmpiW( p, script, len ) && (!p[len] || p[len] == ';')) break; + if (!(p = strchrW( p, ';'))) break; + p++; + } + if (!p || !*p) continue; + } + + if (!country && (flags & 2)) + { + if (!script) id = MAKELANGID( PRIMARYLANGID(id), LANG_NEUTRAL ); + switch (id) + { + case MAKELANGID( LANG_CHINESE, SUBLANG_NEUTRAL ): + case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE ): + *lcid = MAKELCID( 0x7804, SORT_DEFAULT ); + break; + case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ): + case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_MACAU ): + case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ): + *lcid = MAKELCID( 0x7c04, SORT_DEFAULT ); + break; + default: + *lcid = MAKELANGID( PRIMARYLANGID(id), SUBLANG_NEUTRAL ); + break; + } + goto found; + } + } + return STATUS_INVALID_PARAMETER_1; + +found: + TRACE( "%s -> %04x\n", debugstr_w(name), *lcid ); + return STATUS_SUCCESS; +} diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index a42b4fc2482..1cef46953ed 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -807,6 +807,7 @@ @ stdcall RtlLengthSecurityDescriptor(ptr) @ stdcall RtlLengthSid(ptr) @ stdcall RtlLocalTimeToSystemTime(ptr ptr) +@ stdcall RtlLocaleNameToLcid(wstr ptr long) # @ stub RtlLockBootStatusData @ stdcall RtlLockHeap(long) # @ stub RtlLockMemoryStreamRegion diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 758bd49ec6f..e1fb1e9eba7 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -86,6 +86,7 @@ extern void virtual_init_threading(void) DECLSPEC_HIDDEN; extern void fill_cpu_info(void) DECLSPEC_HIDDEN; extern void heap_set_debug_flags( HANDLE handle ) DECLSPEC_HIDDEN; extern void init_unix_codepage(void) DECLSPEC_HIDDEN; +extern void init_locale( HMODULE module ) DECLSPEC_HIDDEN; extern void init_user_process_params( SIZE_T data_size ) DECLSPEC_HIDDEN; extern char **build_envp( const WCHAR *envW ) DECLSPEC_HIDDEN; extern NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS status ) DECLSPEC_HIDDEN; diff --git a/include/winternl.h b/include/winternl.h index 9b8bef0fe7c..ba0de0e6dee 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -2778,6 +2778,7 @@ NTSYSAPI DWORD WINAPI RtlLengthRequiredSid(DWORD); NTSYSAPI ULONG WINAPI RtlLengthSecurityDescriptor(PSECURITY_DESCRIPTOR); NTSYSAPI DWORD WINAPI RtlLengthSid(PSID); NTSYSAPI NTSTATUS WINAPI RtlLocalTimeToSystemTime(const LARGE_INTEGER*,PLARGE_INTEGER); +NTSYSAPI NTSTATUS WINAPI RtlLocaleNameToLcid(const WCHAR*,LCID*,ULONG); NTSYSAPI BOOLEAN WINAPI RtlLockHeap(HANDLE); NTSYSAPI NTSTATUS WINAPI RtlLookupAtomInAtomTable(RTL_ATOM_TABLE,const WCHAR*,RTL_ATOM*); NTSYSAPI NTSTATUS WINAPI RtlMakeSelfRelativeSD(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,LPDWORD);