ntdll: Implement RtlLocaleNameToLcid().

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
stable
Alexandre Julliard 2019-11-19 10:08:27 +01:00
parent 4538a137e0
commit 7ac7a902c8
6 changed files with 221 additions and 5 deletions

View File

@ -30,11 +30,14 @@
#include <stdarg.h>
#include <stdio.h>
#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 */

View File

@ -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 )))

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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);