[Documentation for EnumSystemLocalesA/W](https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-enumsys...) says "If [`LCID_ALTERNATE_SORTS`] is used with either `LCID_INSTALLED` or `LCID_SUPPORTED`, the installed or supported locales are retrieved, **as well as** the alternate sort locale identifiers." (emphasis mine).
Until now, Wine *only* returned alternate sorts in this case, which apparently breaks some versions of NexusDB.
This MR fixes the issue and adds some basic tests to ensure the issue isn't accidentally re-introduced at a later time.
-- v6: kernel32/tests: Add basic tests for EnumSystemLocalesA/W
From: Michael Ehrenreich michideep@gmail.com
Make EnumSystemLocalesA/W return both default and alternate sort order LCIDs when passed both LCID_ALTERNATE_SORTS and LCID_INSTALLED/SUPPORTED. --- dlls/kernelbase/locale.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/dlls/kernelbase/locale.c b/dlls/kernelbase/locale.c index 0dc90fbe25b..651f45be8c2 100644 --- a/dlls/kernelbase/locale.c +++ b/dlls/kernelbase/locale.c @@ -5081,14 +5081,19 @@ BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD f char name[10]; DWORD i;
+ if (!flags) + flags = LCID_SUPPORTED; + for (i = 0; i < locale_table->nb_lcnames; i++) { if (!lcnames_index[i].name) continue; /* skip invariant locale */ if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */ if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */ if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */ - if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS)) - continue; /* skip alternate sorts */ + if (SORTIDFROMLCID( lcnames_index[i].id ) != SORT_DEFAULT && !(flags & LCID_ALTERNATE_SORTS)) + continue; /* skip alternate sorts if not requested */ + if (SORTIDFROMLCID( lcnames_index[i].id ) == SORT_DEFAULT && !(flags & (LCID_INSTALLED | LCID_SUPPORTED))) + continue; /* skip default sorts if not requested */ sprintf( name, "%08x", lcnames_index[i].id ); if (!proc( name )) break; } @@ -5104,14 +5109,19 @@ BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD f WCHAR name[10]; DWORD i;
+ if (!flags) + flags = LCID_SUPPORTED; + for (i = 0; i < locale_table->nb_lcnames; i++) { if (!lcnames_index[i].name) continue; /* skip invariant locale */ if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */ if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */ if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */ - if (!SORTIDFROMLCID( lcnames_index[i].id ) != !(flags & LCID_ALTERNATE_SORTS)) - continue; /* skip alternate sorts */ + if (SORTIDFROMLCID( lcnames_index[i].id ) != SORT_DEFAULT && !(flags & LCID_ALTERNATE_SORTS)) + continue; /* skip alternate sorts if not requested */ + if (SORTIDFROMLCID( lcnames_index[i].id ) == SORT_DEFAULT && !(flags & (LCID_INSTALLED | LCID_SUPPORTED))) + continue; /* skip default sorts if not requested */ swprintf( name, ARRAY_SIZE(name), L"%08lx", lcnames_index[i].id ); if (!proc( name )) break; }
From: Michael Ehrenreich michideep@gmail.com
--- dlls/kernel32/tests/locale.c | 183 +++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+)
diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 66dd73302ee..24250d93602 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -59,6 +59,8 @@ static INT (WINAPI *pGetDateFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWST static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROCA, DWORD, LONG_PTR); static BOOL (WINAPI *pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROCA, LGRPID, DWORD, LONG_PTR); static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROCA, DWORD, LONG_PTR); +static BOOL (WINAPI *pEnumSystemLocalesA)(LOCALE_ENUMPROCA, DWORD); +static BOOL (WINAPI *pEnumSystemLocalesW)(LOCALE_ENUMPROCW, DWORD); 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); @@ -122,6 +124,8 @@ static void InitFunctionPointers(void) X(LCMapStringEx); X(IsValidLanguageGroup); X(EnumUILanguagesA); + X(EnumSystemLocalesA); + X(EnumSystemLocalesW); X(EnumSystemLocalesEx); X(IdnToNameprepUnicode); X(IdnToAscii); @@ -4463,6 +4467,183 @@ static void test_EnumSystemLanguageGroupsA(void) pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_SUPPORTED, 0); }
+LONG default_seen; +LONG alternate_seen; + +static BOOL CALLBACK test_EnumSystemLocalesA_callback(LPSTR str) +{ + LCID lcid; + WORD sortid; + + if (sscanf(str, "%lx", &lcid) != 1) + { + ok(FALSE, "EnumSystemLocalesA callback received unparsable LCID string "%s"\n", str); + return FALSE; + } + + sortid = SORTIDFROMLCID(lcid); + if (sortid == SORT_DEFAULT) + { + default_seen++; + } + else + { + alternate_seen++; + } + + return TRUE; +} + +static BOOL CALLBACK test_EnumSystemLocalesW_callback(LPWSTR str) +{ + LCID lcid; + WORD sortid; + + if (swscanf(str, L"%lx", &lcid) != 1) + { + ok(FALSE, "EnumSystemLocalesW callback received unparsable LCID string "%hs"\n", str); + return FALSE; + } + + sortid = SORTIDFROMLCID(lcid); + if (sortid == SORT_DEFAULT) + { + default_seen++; + } + else + { + alternate_seen++; + } + + return TRUE; +} + +static void test_EnumSystemLocalesA(void) +{ + if (!pEnumSystemLocalesA) + { + win_skip("EnumSystemLocalesA not available"); + return; + } + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesA(test_EnumSystemLocalesA_callback, 0); + ok(default_seen, "EnumSystemLocalesA(..., 0) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(!alternate_seen, "EnumSystemLocalesA(..., 0) returned %ld locales " + "with alternate sort order, expected 0\n", alternate_seen); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesA(test_EnumSystemLocalesA_callback, LCID_INSTALLED); + ok(default_seen, "EnumSystemLocalesA(..., LCID_INSTALLED) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(!alternate_seen, "EnumSystemLocalesA(..., LCID_INSTALLED) returned %ld locales " + "with alternate sort order, expected 0\n", alternate_seen); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesA(test_EnumSystemLocalesA_callback, LCID_SUPPORTED); + ok(default_seen, "EnumSystemLocalesA(..., LCID_SUPPORTED) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(!alternate_seen, "EnumSystemLocalesA(..., LCID_SUPPORTED) returned %ld locales " + "with alternate sort order, expected 0\n", alternate_seen); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesA(test_EnumSystemLocalesA_callback, LCID_ALTERNATE_SORTS); + ok(alternate_seen, "EnumSystemLocalesA(..., LCID_ALTERNATE_SORTS) returned 0 locales " + "with alternate sort order, expected > 0\n"); + ok(!default_seen, "EnumSystemLocalesA(..., LCID_ALTERNATE_SORTS) returned %ld locales " + "with default sort order, expected 0\n", alternate_seen); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesA(test_EnumSystemLocalesA_callback, LCID_INSTALLED | LCID_ALTERNATE_SORTS); + ok(default_seen, "EnumSystemLocalesA(..., LCID_INSTALLED | LCID_ALTERNATE_SORTS) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(alternate_seen, "EnumSystemLocalesA(..., LCID_INSTALLED | LCID_ALTERNATE_SORTS) returned 0 locales " + "with alternate sort order, expected > 0\n"); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesA(test_EnumSystemLocalesA_callback, LCID_SUPPORTED | LCID_ALTERNATE_SORTS); + ok(default_seen, "EnumSystemLocalesA(..., LCID_SUPPORTED | LCID_ALTERNATE_SORTS) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(alternate_seen, "EnumSystemLocalesA(..., LCID_SUPPORTED | LCID_ALTERNATE_SORTS) returned 0 locales " + "with alternate sort order, expected > 0\n"); +} + +static void test_EnumSystemLocalesW(void) +{ + if (!pEnumSystemLocalesW) + { + win_skip("EnumSystemLocalesW not available"); + return; + } + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesW(test_EnumSystemLocalesW_callback, 0); + ok(default_seen, "EnumSystemLocalesW(..., 0) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(!alternate_seen, "EnumSystemLocalesW(..., 0) returned %ld locales " + "with alternate sort order, expected 0\n", alternate_seen); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesW(test_EnumSystemLocalesW_callback, LCID_INSTALLED); + ok(default_seen, "EnumSystemLocalesW(..., LCID_INSTALLED) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(!alternate_seen, "EnumSystemLocalesW(..., LCID_INSTALLED) returned %ld locales " + "with alternate sort order, expected 0\n", alternate_seen); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesW(test_EnumSystemLocalesW_callback, LCID_SUPPORTED); + ok(default_seen, "EnumSystemLocalesW(..., LCID_SUPPORTED) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(!alternate_seen, "EnumSystemLocalesW(..., LCID_SUPPORTED) returned %ld locales " + "with alternate sort order, expected 0\n", alternate_seen); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesW(test_EnumSystemLocalesW_callback, LCID_ALTERNATE_SORTS); + ok(alternate_seen, "EnumSystemLocalesW(..., LCID_ALTERNATE_SORTS) returned 0 locales " + "with alternate sort order, expected > 0\n"); + ok(!default_seen, "EnumSystemLocalesW(..., LCID_ALTERNATE_SORTS) returned %ld locales " + "with default sort order, expected 0\n", alternate_seen); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesW(test_EnumSystemLocalesW_callback, LCID_INSTALLED | LCID_ALTERNATE_SORTS); + ok(default_seen, "EnumSystemLocalesW(..., LCID_INSTALLED | LCID_ALTERNATE_SORTS) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(alternate_seen, "EnumSystemLocalesW(..., LCID_INSTALLED | LCID_ALTERNATE_SORTS) returned 0 locales " + "with alternate sort order, expected > 0\n"); + + default_seen = 0; + alternate_seen = 0; + + pEnumSystemLocalesW(test_EnumSystemLocalesW_callback, LCID_SUPPORTED | LCID_ALTERNATE_SORTS); + ok(default_seen, "EnumSystemLocalesW(..., LCID_SUPPORTED | LCID_ALTERNATE_SORTS) returned 0 locales " + "with default sort order, expected > 0\n"); + ok(alternate_seen, "EnumSystemLocalesW(..., LCID_SUPPORTED | LCID_ALTERNATE_SORTS) returned 0 locales " + "with alternate sort order, expected > 0\n"); +} + static BOOL CALLBACK enum_func( LPWSTR name, DWORD flags, LPARAM lparam ) { if (winetest_debug > 1) @@ -8503,6 +8684,8 @@ START_TEST(locale) test_FoldStringW(); test_ConvertDefaultLocale(); test_EnumSystemLanguageGroupsA(); + test_EnumSystemLocalesA(); + test_EnumSystemLocalesW(); test_EnumSystemLocalesEx(); test_EnumLanguageGroupLocalesA(); test_SetLocaleInfo();