ntdll: Reimplement RtlLocaleNameToLcid() using the locale.nls data.
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
4b0f1163fa
commit
b631c14702
|
@ -2673,15 +2673,6 @@ static void test_LocaleNameToLCID(void)
|
|||
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};
|
||||
static const WCHAR zhhansW[] = {'z','h','-','h','a','n','s',0};
|
||||
static const WCHAR zhHantW[] = {'z','h','-','H','a','n','t',0};
|
||||
static const WCHAR zhhantW[] = {'z','h','-','h','a','n','t',0};
|
||||
static const WCHAR zhcnW[] = {'z','h','-','C','N',0};
|
||||
static const WCHAR zhhkW[] = {'z','h','-','H','K',0};
|
||||
|
||||
if (!pLocaleNameToLCID)
|
||||
{
|
||||
win_skip( "LocaleNameToLCID not available\n" );
|
||||
|
@ -2722,14 +2713,14 @@ static void test_LocaleNameToLCID(void)
|
|||
"Expected lcid == 0, got %08lx, error %ld\n", lcid, GetLastError());
|
||||
|
||||
/* lower-case */
|
||||
lcid = pLocaleNameToLCID(esesW, 0);
|
||||
lcid = pLocaleNameToLCID(L"es-es", 0);
|
||||
ok(lcid == MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT), "Got wrong lcid for es-es: 0x%lx\n", lcid);
|
||||
|
||||
/* english neutral name */
|
||||
lcid = pLocaleNameToLCID(enW, LOCALE_ALLOW_NEUTRAL_NAMES);
|
||||
lcid = pLocaleNameToLCID(L"en", LOCALE_ALLOW_NEUTRAL_NAMES);
|
||||
ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT) ||
|
||||
broken(lcid == 0) /* Vista */, "got 0x%04lx\n", lcid);
|
||||
lcid = pLocaleNameToLCID(enW, 0);
|
||||
lcid = pLocaleNameToLCID(L"en", 0);
|
||||
ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) ||
|
||||
broken(lcid == 0) /* Vista */, "got 0x%04lx\n", lcid);
|
||||
if (lcid)
|
||||
|
@ -2749,52 +2740,52 @@ static void test_LocaleNameToLCID(void)
|
|||
}
|
||||
|
||||
/* zh-Hant has LCID 0x7c04, but LocaleNameToLCID actually returns 0x0c04, which is the LCID of zh-HK */
|
||||
lcid = pLocaleNameToLCID(zhHantW, 0);
|
||||
lcid = pLocaleNameToLCID(L"zh-Hant", 0);
|
||||
ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
|
||||
"%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(zhHantW), lcid);
|
||||
"%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(L"zh-Hant"), lcid);
|
||||
ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHantW), ret);
|
||||
ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(zhHantW), wine_dbgstr_w(buffer));
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-Hant"), ret);
|
||||
ok(!lstrcmpW(L"zh-HK", buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(L"zh-Hant"), wine_dbgstr_w(buffer));
|
||||
/* check that 0x7c04 also works and is mapped to zh-HK */
|
||||
ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE_TRADITIONAL, SUBLANG_CHINESE_TRADITIONAL),
|
||||
buffer, ARRAY_SIZE(buffer), 0);
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHantW), ret);
|
||||
ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(zhHantW), wine_dbgstr_w(buffer));
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-Hant"), ret);
|
||||
ok(!lstrcmpW(L"zh-HK", buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(L"zh-Hant"), wine_dbgstr_w(buffer));
|
||||
|
||||
/* zh-hant */
|
||||
lcid = pLocaleNameToLCID(zhhantW, 0);
|
||||
lcid = pLocaleNameToLCID(L"zh-hant", 0);
|
||||
ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
|
||||
"%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(zhhantW), lcid);
|
||||
"%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(L"zh-hant"), lcid);
|
||||
ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhhantW), ret);
|
||||
ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(zhhantW), wine_dbgstr_w(buffer));
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-hant"), ret);
|
||||
ok(!lstrcmpW(L"zh-HK", buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(L"zh-hant"), wine_dbgstr_w(buffer));
|
||||
|
||||
/* zh-Hans has LCID 0x0004, but LocaleNameToLCID actually returns 0x0804, which is the LCID of zh-CN */
|
||||
lcid = pLocaleNameToLCID(zhHansW, 0);
|
||||
lcid = pLocaleNameToLCID(L"zh-Hans", 0);
|
||||
/* check that LocaleNameToLCID actually returns 0x0804 */
|
||||
ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
|
||||
"%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(zhHansW), lcid);
|
||||
"%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(L"zh-Hans"), lcid);
|
||||
ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHansW), ret);
|
||||
ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(zhHansW), wine_dbgstr_w(buffer));
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-Hans"), ret);
|
||||
ok(!lstrcmpW(L"zh-CN", buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(L"zh-Hans"), wine_dbgstr_w(buffer));
|
||||
/* check that 0x0004 also works and is mapped to zh-CN */
|
||||
ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE, SUBLANG_NEUTRAL), buffer, ARRAY_SIZE(buffer), 0);
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHansW), ret);
|
||||
ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(zhHansW), wine_dbgstr_w(buffer));
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-Hans"), ret);
|
||||
ok(!lstrcmpW(L"zh-CN", buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(L"zh-Hans"), wine_dbgstr_w(buffer));
|
||||
|
||||
/* zh-hans */
|
||||
lcid = pLocaleNameToLCID(zhhansW, 0);
|
||||
lcid = pLocaleNameToLCID(L"zh-hans", 0);
|
||||
ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
|
||||
"%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(zhhansW), lcid);
|
||||
"%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(L"zh-hans"), lcid);
|
||||
ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhhansW), ret);
|
||||
ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(zhhansW), wine_dbgstr_w(buffer));
|
||||
ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-hans"), ret);
|
||||
ok(!lstrcmpW(L"zh-CN", buffer), "%s: got wrong locale name %s\n",
|
||||
wine_dbgstr_w(L"zh-hans"), wine_dbgstr_w(buffer));
|
||||
}
|
||||
|
||||
if (pRtlLocaleNameToLcid)
|
||||
|
@ -2817,16 +2808,31 @@ static void test_LocaleNameToLCID(void)
|
|||
ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), "got %08lx\n", lcid );
|
||||
|
||||
lcid = 0;
|
||||
status = pRtlLocaleNameToLcid( esesW, &lcid, 0 );
|
||||
status = pRtlLocaleNameToLcid( L"es-es", &lcid, 0 );
|
||||
ok( !status, "failed error %lx\n", status );
|
||||
ok( lcid == MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), "got %08lx\n", lcid );
|
||||
|
||||
lcid = 0;
|
||||
status = pRtlLocaleNameToLcid( enW, &lcid, 0 );
|
||||
status = pRtlLocaleNameToLcid( L"de-DE_phoneb", &lcid, 0 );
|
||||
ok( !status, "failed error %lx\n", status );
|
||||
ok( lcid == 0x00010407, "got %08lx\n", lcid );
|
||||
|
||||
lcid = 0;
|
||||
status = pRtlLocaleNameToLcid( L"DE_de-PHONEB", &lcid, 0 );
|
||||
ok( !status || broken( status == STATUS_INVALID_PARAMETER_1 ), "failed error %lx\n", status );
|
||||
if (!status) ok( lcid == 0x00010407, "got %08lx\n", lcid );
|
||||
|
||||
lcid = 0xdeadbeef;
|
||||
status = pRtlLocaleNameToLcid( L"de+de+phoneb", &lcid, 0 );
|
||||
ok( status == STATUS_INVALID_PARAMETER_1, "failed error %lx\n", status );
|
||||
ok( lcid == 0xdeadbeef, "got %08lx\n", lcid );
|
||||
|
||||
lcid = 0;
|
||||
status = pRtlLocaleNameToLcid( L"en", &lcid, 0 );
|
||||
ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
|
||||
status = pRtlLocaleNameToLcid( enW, &lcid, 1 );
|
||||
status = pRtlLocaleNameToLcid( L"en", &lcid, 1 );
|
||||
ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
|
||||
status = pRtlLocaleNameToLcid( enW, &lcid, 2 );
|
||||
status = pRtlLocaleNameToLcid( L"en", &lcid, 2 );
|
||||
ok( !status, "failed error %lx\n", status );
|
||||
ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), "got %08lx\n", lcid );
|
||||
status = pRtlLocaleNameToLcid( L"en-RR", &lcid, 2 );
|
||||
|
|
|
@ -238,6 +238,44 @@ invalid:
|
|||
}
|
||||
|
||||
|
||||
static int compare_locale_names( const WCHAR *n1, const WCHAR *n2 )
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
WCHAR ch1 = casemap_ascii( *n1++ );
|
||||
WCHAR ch2 = casemap_ascii( *n2++ );
|
||||
if (ch1 == '_') ch1 = '-';
|
||||
if (ch2 == '_') ch2 = '-';
|
||||
if (!ch1 || ch1 != ch2) return ch1 - ch2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const NLS_LOCALE_LCNAME_INDEX *find_lcname_entry( const WCHAR *name )
|
||||
{
|
||||
int min = 0, max = locale_table->nb_lcnames - 1;
|
||||
|
||||
if (!name) return NULL;
|
||||
while (min <= max)
|
||||
{
|
||||
int res, pos = (min + max) / 2;
|
||||
const WCHAR *str = locale_strings + lcnames_index[pos].name;
|
||||
res = compare_locale_names( name, str + 1 );
|
||||
if (res < 0) max = pos - 1;
|
||||
else if (res > 0) min = pos + 1;
|
||||
else return &lcnames_index[pos];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const NLS_LOCALE_DATA *get_locale_data( UINT idx )
|
||||
{
|
||||
ULONG offset = locale_table->locales_offset + idx * locale_table->locale_size;
|
||||
return (const NLS_LOCALE_DATA *)((const char *)locale_table + offset);
|
||||
}
|
||||
|
||||
|
||||
void locale_init(void)
|
||||
{
|
||||
LARGE_INTEGER unused;
|
||||
|
@ -1191,110 +1229,12 @@ WCHAR __cdecl towupper( WCHAR ch )
|
|||
*/
|
||||
NTSTATUS WINAPI RtlLocaleNameToLcid( const WCHAR *name, LCID *lcid, ULONG flags )
|
||||
{
|
||||
/* locale name format is: lang[-script][-country][_modifier] */
|
||||
const NLS_LOCALE_LCNAME_INDEX *entry = find_lcname_entry( name );
|
||||
|
||||
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 (wcslen( name ) >= LOCALE_NAME_MAX_LENGTH) return STATUS_INVALID_PARAMETER_1;
|
||||
wcscpy( lang, name );
|
||||
|
||||
if ((p = wcspbrk( lang, L"-_" )) && *p == '-')
|
||||
{
|
||||
*p++ = 0;
|
||||
country = p;
|
||||
if ((p = wcspbrk( p, L"-_" )) && *p == '-')
|
||||
{
|
||||
*p++ = 0;
|
||||
script = country;
|
||||
country = p;
|
||||
p = wcspbrk( p, L"-_" );
|
||||
}
|
||||
if (p) *p = 0; /* FIXME: modifier is ignored */
|
||||
/* second value can be script or country, check length to resolve the ambiguity */
|
||||
if (!script && wcslen( 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) ) && !wcsicmp( name, buf ))
|
||||
{
|
||||
*lcid = MAKELCID( id, SORT_DEFAULT ); /* FIXME: handle sort order */
|
||||
goto found;
|
||||
}
|
||||
|
||||
if (load_string( LOCALE_SISO639LANGNAME, id, buf, ARRAY_SIZE(buf) ) || wcsicmp( lang, buf ))
|
||||
continue;
|
||||
|
||||
if (script)
|
||||
{
|
||||
unsigned int len = wcslen( script );
|
||||
if (load_string( LOCALE_SSCRIPTS, id, buf, ARRAY_SIZE(buf) )) continue;
|
||||
p = buf;
|
||||
while (*p)
|
||||
{
|
||||
if (!wcsnicmp( p, script, len ) && (!p[len] || p[len] == ';')) break;
|
||||
if (!(p = wcschr( 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;
|
||||
case MAKELANGID( LANG_SERBIAN, SUBLANG_NEUTRAL ):
|
||||
*lcid = LANG_SERBIAN_NEUTRAL;
|
||||
break;
|
||||
default:
|
||||
*lcid = MAKELANGID( PRIMARYLANGID(id), SUBLANG_NEUTRAL );
|
||||
break;
|
||||
}
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
return STATUS_INVALID_PARAMETER_1;
|
||||
|
||||
found:
|
||||
if (!entry) return STATUS_INVALID_PARAMETER_1;
|
||||
/* reject neutral locale unless flag 2 is set */
|
||||
if (!(flags & 2) && !get_locale_data( entry->idx )->inotneutral) return STATUS_INVALID_PARAMETER_1;
|
||||
*lcid = entry->id;
|
||||
TRACE( "%s -> %04x\n", debugstr_w(name), *lcid );
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue