diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index ad644cf5a3e..68bab2094e5 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -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 ); diff --git a/dlls/ntdll/locale.c b/dlls/ntdll/locale.c index c60e6f2cf3e..b266bc0af3d 100644 --- a/dlls/ntdll/locale.c +++ b/dlls/ntdll/locale.c @@ -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; }