Resubmission of RtlLCIDToCultureName patch, fixed based on Alexandre's advice.
Signed-off-by: Mark Harmstone mark@harmstone.com --- dlls/ntdll/locale.c | 200 ++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/ntdll.spec | 1 + 2 files changed, 201 insertions(+)
diff --git a/dlls/ntdll/locale.c b/dlls/ntdll/locale.c index b3a225a30c3..1251e247d07 100644 --- a/dlls/ntdll/locale.c +++ b/dlls/ntdll/locale.c @@ -126,6 +126,27 @@ static NTSTATUS load_string( ULONG id, LANGID lang, WCHAR *buffer, ULONG len ) }
+static NTSTATUS load_string_us( ULONG id, LANGID lang, PUNICODE_STRING us ) +{ + 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 >= us->MaximumLength / sizeof(WCHAR)) return STATUS_BUFFER_TOO_SMALL; + us->Length = *p * sizeof(WCHAR); + memcpy( us->Buffer, p + 1, us->Length ); + return STATUS_SUCCESS; +} + + static DWORD mbtowc_size( const CPTABLEINFO *info, LPCSTR str, UINT len ) { DWORD res; @@ -1936,3 +1957,182 @@ NTSTATUS WINAPI RtlIdnToUnicode( DWORD flags, const WCHAR *src, INT srclen, WCHA *dstlen = out; return STATUS_SUCCESS; } + + +/****************************************************************************** + * RtlLCIDToCultureName (NTDLL.@) + */ +BOOLEAN WINAPI RtlLCIDToCultureName( LCID lcid, PUNICODE_STRING string ) +{ + NTSTATUS status; + + /* kernel32 doesn't store information about primary languages */ + + static const struct { + unsigned int lang; + const WCHAR *name; + } primary_langs[] = + { + { LANG_ARABIC, L"ar" }, + { LANG_BULGARIAN, L"bg" }, + { LANG_CATALAN, L"ca" }, + { LANG_CHINESE, L"zh-Hans" }, + { LANG_CZECH, L"cs" }, + { LANG_DANISH, L"da" }, + { LANG_GERMAN, L"de" }, + { LANG_GREEK, L"el" }, + { LANG_ENGLISH, L"en" }, + { LANG_SPANISH, L"es" }, + { LANG_FINNISH, L"fi" }, + { LANG_FRENCH, L"fr" }, + { LANG_HEBREW, L"he" }, + { LANG_HUNGARIAN, L"hu" }, + { LANG_ICELANDIC, L"is" }, + { LANG_ITALIAN, L"it" }, + { LANG_JAPANESE, L"ja" }, + { LANG_KOREAN, L"ko" }, + { LANG_DUTCH, L"nl" }, + { LANG_NORWEGIAN, L"no" }, + { LANG_POLISH, L"pl" }, + { LANG_PORTUGUESE, L"pt" }, + { LANG_ROMANSH, L"rm" }, + { LANG_ROMANIAN, L"ro" }, + { LANG_RUSSIAN, L"ru" }, + { LANG_CROATIAN, L"hr" }, + { LANG_SLOVAK, L"sk" }, + { LANG_ALBANIAN, L"sq" }, + { LANG_SWEDISH, L"sv" }, + { LANG_THAI, L"th" }, + { LANG_TURKISH, L"tr" }, + { LANG_URDU, L"ur" }, + { LANG_INDONESIAN, L"id" }, + { LANG_UKRAINIAN, L"uk" }, + { LANG_BELARUSIAN, L"be" }, + { LANG_SLOVENIAN, L"sl" }, + { LANG_ESTONIAN, L"et" }, + { LANG_LATVIAN, L"lv" }, + { LANG_LITHUANIAN, L"lt" }, + { LANG_TAJIK, L"tg" }, + { LANG_PERSIAN, L"fa" }, + { LANG_VIETNAMESE, L"vi" }, + { LANG_ARMENIAN, L"hy" }, + { LANG_AZERBAIJANI, L"az" }, + { LANG_BASQUE, L"eu" }, + { LANG_LOWER_SORBIAN, L"hsb" }, + { LANG_MACEDONIAN, L"mk" }, + { LANG_TSWANA, L"tn" }, + { LANG_XHOSA, L"xh" }, + { LANG_ZULU, L"zu" }, + { LANG_AFRIKAANS, L"af" }, + { LANG_GEORGIAN, L"ka" }, + { LANG_FAEROESE, L"fo" }, + { LANG_HINDI, L"hi" }, + { LANG_MALTESE, L"mt" }, + { LANG_SAMI, L"se" }, + { LANG_IRISH, L"ga" }, + { LANG_MALAY, L"ms" }, + { LANG_KAZAK, L"kk" }, + { LANG_KYRGYZ, L"ky" }, + { LANG_SWAHILI, L"sw" }, + { LANG_TURKMEN, L"tk" }, + { LANG_UZBEK, L"uz" }, + { LANG_TATAR, L"tt" }, + { LANG_BENGALI, L"bn" }, + { LANG_PUNJABI, L"pa" }, + { LANG_GUJARATI, L"gu" }, + { LANG_ODIA, L"or" }, + { LANG_TAMIL, L"ta" }, + { LANG_TELUGU, L"te" }, + { LANG_KANNADA, L"kn" }, + { LANG_MALAYALAM, L"ml" }, + { LANG_ASSAMESE, L"as" }, + { LANG_MARATHI, L"mr" }, + { LANG_SANSKRIT, L"sa" }, + { LANG_MONGOLIAN, L"mn" }, + { LANG_TIBETAN, L"bo" }, + { LANG_WELSH, L"cy" }, + { LANG_KHMER, L"km" }, + { LANG_LAO, L"lo" }, + { LANG_GALICIAN, L"gl" }, + { LANG_KONKANI, L"kok" }, + { LANG_MANIPURI, L"mni" }, + { LANG_SINDHI, L"sd" }, + { LANG_SYRIAC, L"syr" }, + { LANG_SINHALESE, L"si" }, + { LANG_INUKTITUT, L"iu" }, + { LANG_AMHARIC, L"am" }, + { LANG_TAMAZIGHT, L"tzm" }, + { LANG_NEPALI, L"ne" }, + { LANG_FRISIAN, L"fy" }, + { LANG_PASHTO, L"ps" }, + { LANG_FILIPINO, L"fil" }, + { LANG_DIVEHI, L"dv" }, + { LANG_HAUSA, L"ha" }, + { LANG_YORUBA, L"yo" }, + { LANG_QUECHUA, L"quz" }, + { LANG_SOTHO, L"nso" }, + { LANG_BASHKIR, L"ba" }, + { LANG_LUXEMBOURGISH, L"lb" }, + { LANG_GREENLANDIC, L"kl" }, + { LANG_IGBO, L"ig" }, + { LANG_YI, L"ii" }, + { LANG_MAPUDUNGUN, L"arn" }, + { LANG_MOHAWK, L"moh" }, + { LANG_BRETON, L"br" }, + { LANG_INVARIANT, L"" }, + { LANG_UIGHUR, L"ug" }, + { LANG_MAORI, L"mi" }, + { LANG_OCCITAN, L"oc" }, + { LANG_CORSICAN, L"co" }, + { LANG_ALSATIAN, L"gsw" }, + { LANG_SAKHA, L"sah" }, + { LANG_KINYARWANDA, L"rw" }, + { LANG_WOLOF, L"wo" }, + { LANG_DARI, L"prs" }, + { 0, NULL } + }; + + if (lcid == 0) + return FALSE; + else if (lcid == LOCALE_USER_DEFAULT) + { + status = NtQueryDefaultLocale(TRUE, &lcid); + if (!NT_SUCCESS(status)) + return FALSE; + } + else if (lcid == LOCALE_SYSTEM_DEFAULT) + { + status = NtQueryDefaultLocale(FALSE, &lcid); + if (!NT_SUCCESS(status)) + return FALSE; + } + + if (SUBLANGID(lcid) == SUBLANG_NEUTRAL) + { + unsigned int i = 0; + + while (primary_langs[i].lang != 0) + { + if (primary_langs[i].lang == PRIMARYLANGID(lcid)) + { + size_t len = wcslen( primary_langs[i].name ); + + if (string->MaximumLength < len * sizeof(WCHAR)) + return FALSE; + + string->Length = len * sizeof(WCHAR); + memcpy( string->Buffer, primary_langs[i].name, string->Length ); + + return TRUE; + } + + i++; + } + + return FALSE; + } + + status = load_string_us( LOCALE_SNAME, lcid, string ); + + return NT_SUCCESS(status) ? TRUE : FALSE; +} diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index c8622ba21f4..d27aaf1da58 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -827,6 +827,7 @@ @ stdcall -arch=win32 -ret64 RtlLargeIntegerShiftRight(int64 long) @ stdcall -arch=win32 -ret64 RtlLargeIntegerSubtract(int64 int64) @ stdcall RtlLargeIntegerToChar(ptr long long ptr) +@ stdcall RtlLCIDToCultureName(long ptr) @ stdcall RtlLeaveCriticalSection(ptr) @ stdcall RtlLengthRequiredSid(long) @ stdcall RtlLengthSecurityDescriptor(ptr)
Signed-off-by: Mark Harmstone mark@harmstone.com --- dlls/ntdll/tests/Makefile.in | 1 + dlls/ntdll/tests/mui.c | 451 +++++++++++++++++++++++++++++++++++ 2 files changed, 452 insertions(+) create mode 100644 dlls/ntdll/tests/mui.c
diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in index ed15c51339f..e8896fe25cb 100644 --- a/dlls/ntdll/tests/Makefile.in +++ b/dlls/ntdll/tests/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ generated.c \ info.c \ large_int.c \ + mui.c \ om.c \ path.c \ pipe.c \ diff --git a/dlls/ntdll/tests/mui.c b/dlls/ntdll/tests/mui.c new file mode 100644 index 00000000000..488dfc02033 --- /dev/null +++ b/dlls/ntdll/tests/mui.c @@ -0,0 +1,451 @@ +/* + * Unit test suite for MUI functions + * + * Copyright 2021 Mark Harmstone + * + * 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 "ntdll_test.h" + +static BOOLEAN (NTAPI *pRtlLCIDToCultureName)(LCID, PUNICODE_STRING); +static NTSTATUS (NTAPI *pNtQueryDefaultLocale)(BOOLEAN, PLCID); +static LONG (NTAPI *pRtlCompareUnicodeString)(PCUNICODE_STRING, PCUNICODE_STRING, BOOLEAN); + +static const char *debugstr_us( const UNICODE_STRING *us ) +{ + if (!us) return "<null>"; + return debugstr_wn( us->Buffer, us->Length / sizeof(WCHAR) ); +} + +static void test_lcid_to_culture_name(void) +{ + struct lang + { + LCID lcid; + const WCHAR *name; + }; + + static const struct lang list[] = + { + { 0x0001, L"ar" }, + { 0x0002, L"bg" }, + { 0x0003, L"ca" }, + { 0x0004, L"zh-Hans" }, + { 0x0005, L"cs" }, + { 0x0006, L"da" }, + { 0x0007, L"de" }, + { 0x0008, L"el" }, + { 0x0009, L"en" }, + { 0x000a, L"es" }, + { 0x000b, L"fi" }, + { 0x000c, L"fr" }, + { 0x000d, L"he" }, + { 0x000e, L"hu" }, + { 0x000f, L"is" }, + { 0x0010, L"it" }, + { 0x0011, L"ja" }, + { 0x0012, L"ko" }, + { 0x0013, L"nl" }, + { 0x0014, L"no" }, + { 0x0015, L"pl" }, + { 0x0016, L"pt" }, + { 0x0017, L"rm" }, + { 0x0018, L"ro" }, + { 0x0019, L"ru" }, + { 0x001a, L"hr" }, + { 0x001b, L"sk" }, + { 0x001c, L"sq" }, + { 0x001d, L"sv" }, + { 0x001e, L"th" }, + { 0x001f, L"tr" }, + { 0x0020, L"ur" }, + { 0x0021, L"id" }, + { 0x0022, L"uk" }, + { 0x0023, L"be" }, + { 0x0024, L"sl" }, + { 0x0025, L"et" }, + { 0x0026, L"lv" }, + { 0x0027, L"lt" }, + { 0x0028, L"tg" }, + { 0x0029, L"fa" }, + { 0x002a, L"vi" }, + { 0x002b, L"hy" }, + { 0x002c, L"az" }, + { 0x002d, L"eu" }, + { 0x002e, L"hsb" }, + { 0x002f, L"mk" }, + { 0x0032, L"tn" }, + { 0x0034, L"xh" }, + { 0x0035, L"zu" }, + { 0x0036, L"af" }, + { 0x0037, L"ka" }, + { 0x0038, L"fo" }, + { 0x0039, L"hi" }, + { 0x003a, L"mt" }, + { 0x003b, L"se" }, + { 0x003c, L"ga" }, + { 0x003e, L"ms" }, + { 0x003f, L"kk" }, + { 0x0040, L"ky" }, + { 0x0041, L"sw" }, + { 0x0042, L"tk" }, + { 0x0043, L"uz" }, + { 0x0044, L"tt" }, + { 0x0045, L"bn" }, + { 0x0046, L"pa" }, + { 0x0047, L"gu" }, + { 0x0048, L"or" }, + { 0x0049, L"ta" }, + { 0x004a, L"te" }, + { 0x004b, L"kn" }, + { 0x004c, L"ml" }, + { 0x004d, L"as" }, + { 0x004e, L"mr" }, + { 0x004f, L"sa" }, + { 0x0050, L"mn" }, + { 0x0051, L"bo" }, + { 0x0052, L"cy" }, + { 0x0053, L"km" }, + { 0x0054, L"lo" }, + { 0x0056, L"gl" }, + { 0x0057, L"kok" }, + { 0x005a, L"syr" }, + { 0x005b, L"si" }, + { 0x005d, L"iu" }, + { 0x005e, L"am" }, + { 0x005f, L"tzm" }, + { 0x0061, L"ne" }, + { 0x0062, L"fy" }, + { 0x0063, L"ps" }, + { 0x0064, L"fil" }, + { 0x0065, L"dv" }, + { 0x0068, L"ha" }, + { 0x006a, L"yo" }, + { 0x006b, L"quz" }, + { 0x006c, L"nso" }, + { 0x006d, L"ba" }, + { 0x006e, L"lb" }, + { 0x006f, L"kl" }, + { 0x0070, L"ig" }, + { 0x0078, L"ii" }, + { 0x007a, L"arn" }, + { 0x007c, L"moh" }, + { 0x007e, L"br" }, + { 0x007f, L"" }, + { 0x0080, L"ug" }, + { 0x0081, L"mi" }, + { 0x0082, L"oc" }, + { 0x0083, L"co" }, + { 0x0084, L"gsw" }, + { 0x0085, L"sah" }, + { 0x0087, L"rw" }, + { 0x0088, L"wo" }, + { 0x008c, L"prs" }, + { 0x0401, L"ar-SA" }, + { 0x0402, L"bg-BG" }, + { 0x0403, L"ca-ES" }, + { 0x0404, L"zh-TW" }, + { 0x0405, L"cs-CZ" }, + { 0x0406, L"da-DK" }, + { 0x0407, L"de-DE" }, + { 0x0408, L"el-GR" }, + { 0x0409, L"en-US" }, + { 0x040a, L"es-ES_tradnl" }, + { 0x040b, L"fi-FI" }, + { 0x040c, L"fr-FR" }, + { 0x040d, L"he-IL" }, + { 0x040e, L"hu-HU" }, + { 0x040f, L"is-IS" }, + { 0x0410, L"it-IT" }, + { 0x0411, L"ja-JP" }, + { 0x0412, L"ko-KR" }, + { 0x0413, L"nl-NL" }, + { 0x0414, L"nb-NO" }, + { 0x0415, L"pl-PL" }, + { 0x0416, L"pt-BR" }, + { 0x0417, L"rm-CH" }, + { 0x0418, L"ro-RO" }, + { 0x0419, L"ru-RU" }, + { 0x041a, L"hr-HR" }, + { 0x041b, L"sk-SK" }, + { 0x041c, L"sq-AL" }, + { 0x041d, L"sv-SE" }, + { 0x041e, L"th-TH" }, + { 0x041f, L"tr-TR" }, + { 0x0420, L"ur-PK" }, + { 0x0421, L"id-ID" }, + { 0x0422, L"uk-UA" }, + { 0x0423, L"be-BY" }, + { 0x0424, L"sl-SI" }, + { 0x0425, L"et-EE" }, + { 0x0426, L"lv-LV" }, + { 0x0427, L"lt-LT" }, + { 0x0429, L"fa-IR" }, + { 0x042a, L"vi-VN" }, + { 0x042b, L"hy-AM" }, + { 0x042c, L"az-Latn-AZ" }, + { 0x042d, L"eu-ES" }, + { 0x042f, L"mk-MK" }, + { 0x0436, L"af-ZA" }, + { 0x0437, L"ka-GE" }, + { 0x0438, L"fo-FO" }, + { 0x0439, L"hi-IN" }, + { 0x043e, L"ms-MY" }, + { 0x043f, L"kk-KZ" }, + { 0x0440, L"ky-KG" }, + { 0x0441, L"sw-KE" }, + { 0x0443, L"uz-Latn-UZ" }, + { 0x0444, L"tt-RU" }, + { 0x0445, L"bn-IN" }, + { 0x0446, L"pa-IN" }, + { 0x0447, L"gu-IN" }, + { 0x0449, L"ta-IN" }, + { 0x044a, L"te-IN" }, + { 0x044b, L"kn-IN" }, + { 0x044e, L"mr-IN" }, + { 0x044f, L"sa-IN" }, + { 0x0450, L"mn-MN" }, + { 0x0452, L"cy-GB" }, + { 0x0456, L"gl-ES" }, + { 0x0457, L"kok-IN" }, + { 0x045a, L"syr-SY" }, + { 0x045b, L"si-LK" }, + { 0x0461, L"ne-NP" }, + { 0x0465, L"dv-MV" }, + { 0x047e, L"br-FR" }, + { 0x0801, L"ar-IQ" }, + { 0x0804, L"zh-CN" }, + { 0x0807, L"de-CH" }, + { 0x0809, L"en-GB" }, + { 0x080a, L"es-MX" }, + { 0x080c, L"fr-BE" }, + { 0x0810, L"it-CH" }, + { 0x0813, L"nl-BE" }, + { 0x0814, L"nn-NO" }, + { 0x0816, L"pt-PT" }, + { 0x081a, L"sr-Latn-CS" }, + { 0x081d, L"sv-FI" }, + { 0x082c, L"az-Cyrl-AZ" }, + { 0x083c, L"ga-IE" }, + { 0x083e, L"ms-BN" }, + { 0x0843, L"uz-Cyrl-UZ" }, + { 0x0c01, L"ar-EG" }, + { 0x0c04, L"zh-HK" }, + { 0x0c07, L"de-AT" }, + { 0x0c09, L"en-AU" }, + { 0x0c0a, L"es-ES" }, + { 0x0c0c, L"fr-CA" }, + { 0x1001, L"ar-LY" }, + { 0x1004, L"zh-SG" }, + { 0x1007, L"de-LU" }, + { 0x1009, L"en-CA" }, + { 0x100a, L"es-GT" }, + { 0x100c, L"fr-CH" }, + { 0x1401, L"ar-DZ" }, + { 0x1404, L"zh-MO" }, + { 0x1407, L"de-LI" }, + { 0x1409, L"en-NZ" }, + { 0x140a, L"es-CR" }, + { 0x140c, L"fr-LU" }, + { 0x1801, L"ar-MA" }, + { 0x1809, L"en-IE" }, + { 0x180a, L"es-PA" }, + { 0x180c, L"fr-MC" }, + { 0x1c01, L"ar-TN" }, + { 0x1c09, L"en-ZA" }, + { 0x1c0a, L"es-DO" }, + { 0x2001, L"ar-OM" }, + { 0x2009, L"en-JM" }, + { 0x200a, L"es-VE" }, + { 0x2401, L"ar-YE" }, + { 0x2409, L"en-029" }, + { 0x240a, L"es-CO" }, + { 0x2801, L"ar-SY" }, + { 0x2809, L"en-BZ" }, + { 0x280a, L"es-PE" }, + { 0x2c01, L"ar-JO" }, + { 0x2c09, L"en-TT" }, + { 0x2c0a, L"es-AR" }, + { 0x3001, L"ar-LB" }, + { 0x3009, L"en-ZW" }, + { 0x300a, L"es-EC" }, + { 0x3401, L"ar-KW" }, + { 0x3409, L"en-PH" }, + { 0x340a, L"es-CL" }, + { 0x3801, L"ar-AE" }, + { 0x380a, L"es-UY" }, + { 0x3c01, L"ar-BH" }, + { 0x3c0a, L"es-PY" }, + { 0x4001, L"ar-QA" }, + { 0x400a, L"es-BO" }, + { 0x440a, L"es-SV" }, + { 0x480a, L"es-HN" }, + { 0x4c0a, L"es-NI" }, + { 0x500a, L"es-PR" }, + { 0, NULL } + }; + + /* Languages introduced after Vista */ + static const struct lang opt_list[] = + { + { 0x0058, L"mni" }, + { 0x0059, L"sd" }, + { 0x0491, L"gd-GB" }, + { 0x241a, L"sr-Latn-RS" }, + { 0, NULL } + }; + + unsigned int i; + WCHAR buf[255]; + UNICODE_STRING us; + NTSTATUS nts; + LCID user_lcid, system_lcid; + + if (!pRtlLCIDToCultureName) + { + win_skip("RtlLCIDToCultureName not supported\n"); + return; + } + + us.Buffer = buf; + us.Length = us.MaximumLength = sizeof(buf); + + i = 0; + while (list[i].name) + { + BOOLEAN ret; + + ret = pRtlLCIDToCultureName(list[i].lcid, &us); + + ok(ret, "RtlLCIDTOCultureName failed for LCID %04x\n", list[i].lcid); + + if (ret) + { + ok(us.Length == wcslen(list[i].name) * sizeof(WCHAR) + && !memcmp(us.Buffer, list[i].name, us.Length), + "RtlLCIDTOCultureName returned wrong value for LCID %04x (%s, expected %s)\n", + list[i].lcid, debugstr_us(&us), debugstr_w(list[i].name)); + } + + i++; + } + + i = 0; + while (opt_list[i].name) + { + BOOLEAN ret; + + ret = pRtlLCIDToCultureName(opt_list[i].lcid, &us); + + if (!ret) + { + win_skip("RtlLCIDTOCultureName does not recognize LCID %04x\n", + opt_list[i].lcid); + } + else + { + ok(us.Length == wcslen(opt_list[i].name) * sizeof(WCHAR) + && !memcmp(us.Buffer, opt_list[i].name, us.Length), + "RtlLCIDTOCultureName returned wrong value for LCID %04x (%s, expected %s)\n", + opt_list[i].lcid, debugstr_us(&us), debugstr_w(opt_list[i].name)); + } + + i++; + } + + if (!pNtQueryDefaultLocale) + { + win_skip("NtQueryDefaultLocale not supported\n"); + return; + } + + nts = pNtQueryDefaultLocale(TRUE, &user_lcid); + + ok(NT_SUCCESS(nts), "NtQueryDefaultLocale(TRUE) returned %08x, expected STATUS_SUCCESS\n", + nts); + + if (NT_SUCCESS(nts)) + { + WCHAR buf2[255]; + UNICODE_STRING us2; + BOOLEAN ret; + + us2.Buffer = buf2; + us2.Length = us2.MaximumLength = sizeof(buf2); + + ret = pRtlLCIDToCultureName(user_lcid, &us2); + + ok(ret, "RtlLCIDTOCultureName failed for LCID %04x\n", list[i].lcid); + + if (ret) + { + ret = pRtlLCIDToCultureName(LOCALE_USER_DEFAULT, &us); + + ok(ret, "RtlLCIDTOCultureName failed for LOCALE_USER_DEFAULT\n"); + + if (ret) + { + ok(!pRtlCompareUnicodeString(&us, &us2, FALSE), + "RtlLCIDTOCultureName returned incorrect value for LOCALE_USER_DEFAULT (%s, expected %s)\n", + debugstr_us(&us), debugstr_us(&us2)); + } + } + } + + nts = pNtQueryDefaultLocale(FALSE, &system_lcid); + + ok(NT_SUCCESS(nts), "NtQueryDefaultLocale(FALSE) returned %08x, expected STATUS_SUCCESS\n", + nts); + + if (NT_SUCCESS(nts)) + { + WCHAR buf2[255]; + UNICODE_STRING us2; + BOOLEAN ret; + + us2.Buffer = buf2; + us2.Length = us2.MaximumLength = sizeof(buf2); + + ret = pRtlLCIDToCultureName(system_lcid, &us2); + + ok(ret, "RtlLCIDTOCultureName failed for LCID %04x\n", list[i].lcid); + + if (ret) + { + ret = pRtlLCIDToCultureName(LOCALE_SYSTEM_DEFAULT, &us); + + ok(ret, "RtlLCIDTOCultureName failed for LOCALE_SYSTEM_DEFAULT\n"); + + if (ret) + { + ok(!pRtlCompareUnicodeString(&us, &us2, FALSE), + "RtlLCIDTOCultureName returned incorrect value for LOCALE_SYSTEM_DEFAULT (%s, expected %s)\n", + debugstr_us(&us), debugstr_us(&us2)); + } + } + } +} + +START_TEST(mui) +{ + HMODULE hntdll = GetModuleHandleA( "ntdll.dll" ); + + pRtlCompareUnicodeString = (void*)GetProcAddress( hntdll, "RtlCompareUnicodeString" ); + pRtlLCIDToCultureName = (void*)GetProcAddress( hntdll, "RtlLCIDToCultureName" ); + pNtQueryDefaultLocale = (void*)GetProcAddress( hntdll, "NtQueryDefaultLocale" ); + + test_lcid_to_culture_name(); +}
Mark Harmstone mark@harmstone.com writes:
diff --git a/dlls/ntdll/tests/mui.c b/dlls/ntdll/tests/mui.c new file mode 100644 index 00000000000..488dfc02033 --- /dev/null +++ b/dlls/ntdll/tests/mui.c @@ -0,0 +1,451 @@ +/*
- Unit test suite for MUI functions
- Copyright 2021 Mark Harmstone
- 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
- */
There is nothing MUI-specific about RtlLCIDToCultureName. The tests should go along with other similar functions in dlls/kernel32/tests/locale.c. It should also reuse the existing test data, there's no need to do exhaustive testing of all possible locales.
On 8/4/21 6:06 pm, Alexandre Julliard wrote:
There is nothing MUI-specific about RtlLCIDToCultureName. The tests should go along with other similar functions in dlls/kernel32/tests/locale.c. It should also reuse the existing test data, there's no need to do exhaustive testing of all possible locales.
That's because my full MUI test is some 1400 lines long, which to my mind is substantial enough to warrant its own test...
Also, you want a ntdll function to be tested in the kernel32 test suite? That seems very counter-intuitive.
Finally, one thing I discovered, but left out of the test, is that Wine's custom kw-GB uses what's now a valid LCID for another language. You blatantly need full testing of all possible locales *somewhere*.
Mark Harmstone mark@harmstone.com writes:
On 8/4/21 6:06 pm, Alexandre Julliard wrote:
There is nothing MUI-specific about RtlLCIDToCultureName. The tests should go along with other similar functions in dlls/kernel32/tests/locale.c. It should also reuse the existing test data, there's no need to do exhaustive testing of all possible locales.
That's because my full MUI test is some 1400 lines long, which to my mind is substantial enough to warrant its own test...
I expect that if you put the various bits of your tests in the appropriate files, it will get smaller. For instance the loader tests already have tools to generate PE files, resources could be generated by the resource compiler, etc.
It can make sense to create your own files when you are developing, it makes life easier, especially if you are not sure where things will end up. But when merging upstream you need to make an effort to fit your code back into the existing framework.
Also, you want a ntdll function to be tested in the kernel32 test suite? That seems very counter-intuitive.
It's not very intuitive, but it happens quite a bit. Since kernel32 functions are often a thin wrapper around ntdll ones, it's easier to test both functions once, instead of duplicating the test data.
Finally, one thing I discovered, but left out of the test, is that Wine's custom kw-GB uses what's now a valid LCID for another language. You blatantly need full testing of all possible locales *somewhere*.
I don't think duplicating the entire locale data inside the tests is especially useful, but if you want to do it, locale.c is the logical place for it.
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=88352
Your paranoid android.
=== debiant2 (32 bit Chinese:China report) ===
ntdll: om.c:2305: Test failed: got 88
=== debiant2 (32 bit WoW report) ===
ntdll: om.c:2305: Test failed: got 89 om.c:2305: Test failed: got 87
Mark Harmstone mark@harmstone.com writes:
+static NTSTATUS load_string_us( ULONG id, LANGID lang, PUNICODE_STRING us ) +{
- 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 >= us->MaximumLength / sizeof(WCHAR)) return STATUS_BUFFER_TOO_SMALL;
- us->Length = *p * sizeof(WCHAR);
- memcpy( us->Buffer, p + 1, us->Length );
- return STATUS_SUCCESS;
+}
You could probably reuse the existing helper. This would need some tests about the behavior on short buffers.
- /* kernel32 doesn't store information about primary languages */
LOCALE_SPARENT seems pretty close.
On 8/4/21 6:04 pm, Alexandre Julliard wrote:
You could probably reuse the existing helper. This would need some tests about the behavior on short buffers.
Why? UNICODE_STRINGs don't need to be null-terminated.
- /* kernel32 doesn't store information about primary languages */
LOCALE_SPARENT seems pretty close.
Yes, but that's still indexed by lang-sublang pair. You'd have to loop through every language, which seems overkill just to avoid a bit of code duplication. And you run into the problem where LANG_CROATIAN == LANG_SERBIAN, but they still have separate strings.
Mark Harmstone mark@harmstone.com writes:
On 8/4/21 6:04 pm, Alexandre Julliard wrote:
You could probably reuse the existing helper. This would need some tests about the behavior on short buffers.
Why? UNICODE_STRINGs don't need to be null-terminated.
No but they can be. Like I said this would need tests.
- /* kernel32 doesn't store information about primary languages */
LOCALE_SPARENT seems pretty close.
Yes, but that's still indexed by lang-sublang pair. You'd have to loop through every language, which seems overkill just to avoid a bit of code duplication. And you run into the problem where LANG_CROATIAN == LANG_SERBIAN, but they still have separate strings.
Using the existing data is not overkill. We can't have every language function have its own language table. It doesn't matter if the results are not 100% identical to Windows, what's important is that they are coherent across locale functions.