[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.
From: Michael Ehrenreich michideep@gmail.com
--- 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/kernelbase/tests/Makefile.in | 1 + dlls/kernelbase/tests/locale.c | 222 ++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 dlls/kernelbase/tests/locale.c
diff --git a/dlls/kernelbase/tests/Makefile.in b/dlls/kernelbase/tests/Makefile.in index 654cb9d5aed..cefd9c51e84 100644 --- a/dlls/kernelbase/tests/Makefile.in +++ b/dlls/kernelbase/tests/Makefile.in @@ -2,6 +2,7 @@ TESTDLL = kernelbase.dll
SOURCES = \ file.c \ + locale.c \ path.c \ process.c \ rsrc.rc \ diff --git a/dlls/kernelbase/tests/locale.c b/dlls/kernelbase/tests/locale.c new file mode 100644 index 00000000000..f54c529703f --- /dev/null +++ b/dlls/kernelbase/tests/locale.c @@ -0,0 +1,222 @@ +/* + * Locale tests + * + * Copyright 2024 Michael Ehrenreich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> +#include <stdlib.h> + +#include <ntstatus.h> +#define WIN32_NO_STATUS +#include <windef.h> +#include <winbase.h> +#include <winnls.h> + +#include "wine/test.h" + +static BOOL (WINAPI *pEnumSystemLocalesA)(LOCALE_ENUMPROCA, DWORD); +static BOOL (WINAPI *pEnumSystemLocalesW)(LOCALE_ENUMPROCW, DWORD); + +LONG default_seen; +LONG alternate_seen; + +static BOOL WINAPI test_EnumSystemLocalesA_callback(LPSTR str) +{ + LCID lcid; + WORD sortid; + + if (sscanf(str, "%lx", &lcid) != 1) + { + ok(FALSE, "EnumSystemLocalesA callback received unparseable LCID string "%s"\n", str); + return FALSE; + } + + sortid = SORTIDFROMLCID(lcid); + if (sortid == SORT_DEFAULT) + { + default_seen++; + } + else + { + alternate_seen++; + } + + return TRUE; +} + +static BOOL WINAPI test_EnumSystemLocalesW_callback(LPWSTR str) +{ + LCID lcid; + WORD sortid; + + if (swscanf(str, L"%lx", &lcid) != 1) + { + ok(FALSE, "EnumSystemLocalesW callback received unparseable 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"); +} + +START_TEST(locale) +{ + HMODULE hmod; + + hmod = LoadLibraryA("kernelbase.dll"); + pEnumSystemLocalesA = (void *)GetProcAddress(hmod, "EnumSystemLocalesA"); + pEnumSystemLocalesW = (void *)GetProcAddress(hmod, "EnumSystemLocalesW"); + + test_EnumSystemLocalesA(); + test_EnumSystemLocalesW(); +}