kernelbase: Use linguistic case table for LCMAP_LINGUISTIC_CASING.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
feature/deterministic
Alexandre Julliard 2020-03-18 12:00:02 +01:00
parent 7ad740cd29
commit b780e5f5b1
2 changed files with 127 additions and 32 deletions

View File

@ -6471,7 +6471,6 @@ static void test_SpecialCasing(void)
WCHAR ch;
WCHAR exp; /* 0 if self */
WCHAR exp_ling; /* 0 if exp */
BOOL todo;
} tests[] = {
{deDEW, LCMAP_UPPERCASE, 0x00DF}, /* LATIN SMALL LETTER SHARP S */
@ -6580,29 +6579,29 @@ static void test_SpecialCasing(void)
{enUSW, LCMAP_UPPERCASE, 'i', 'I'}, /* LATIN SMALL LETTER I */
{ltLTW, LCMAP_UPPERCASE, 'i', 'I'}, /* LATIN SMALL LETTER I */
{trTRW, LCMAP_UPPERCASE, 'i', 'I', 0x0130, TRUE}, /* LATIN SMALL LETTER I */
{TRTRW, LCMAP_UPPERCASE, 'i', 'I', 0x0130, TRUE}, /* LATIN SMALL LETTER I */
{azCyrlazW, LCMAP_UPPERCASE, 'i', 'I', 0x0130, TRUE}, /* LATIN SMALL LETTER I */
{azLatnazW, LCMAP_UPPERCASE, 'i', 'I', 0x0130, TRUE}, /* LATIN SMALL LETTER I */
{trTRW, LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
{TRTRW, LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
{azCyrlazW, LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
{azLatnazW, LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
{enUSW, LCMAP_LOWERCASE, 'I', 'i'}, /* LATIN CAPITAL LETTER I */
{ltLTW, LCMAP_LOWERCASE, 'I', 'i'}, /* LATIN CAPITAL LETTER I */
{trTRW, LCMAP_LOWERCASE, 'I', 'i', 0x0131, TRUE}, /* LATIN CAPITAL LETTER I */
{TRTRW, LCMAP_LOWERCASE, 'I', 'i', 0x0131, TRUE}, /* LATIN CAPITAL LETTER I */
{azCyrlazW, LCMAP_LOWERCASE, 'I', 'i', 0x0131, TRUE}, /* LATIN CAPITAL LETTER I */
{azLatnazW, LCMAP_LOWERCASE, 'I', 'i', 0x0131, TRUE}, /* LATIN CAPITAL LETTER I */
{trTRW, LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
{TRTRW, LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
{azCyrlazW, LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
{azLatnazW, LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
{enUSW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{trTRW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{TRTRW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{azCyrlazW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{azLatnazW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{enUSW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{trTRW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{TRTRW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{azCyrlazW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{azLatnazW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{enUSW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
{trTRW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
{TRTRW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
{azCyrlazW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
{azLatnazW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
{enUSW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
{trTRW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
{TRTRW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
{azCyrlazW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
{azLatnazW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
};
if (!pLCMapStringEx)
@ -6628,7 +6627,6 @@ static void test_SpecialCasing(void)
ok(ret == 1, "expected 1, got %d for %04x for %s\n", ret, tests[i].ch,
wine_dbgstr_w(tests[i].lang));
exp = tests[i].exp_ling ? tests[i].exp_ling : exp;
todo_wine_if(tests[i].todo)
ok(buffer[0] == exp || broken(buffer[0] != exp),
"expected %04x, got %04x for %04x for %s\n",
exp, buffer[0], tests[i].ch, wine_dbgstr_w(tests[i].lang));

View File

@ -576,12 +576,34 @@ static unsigned int nb_codepages;
static struct norm_table *norm_info;
struct sortguid
{
GUID id; /* sort GUID */
DWORD flags; /* flags */
DWORD compr; /* offset to compression table */
DWORD except; /* exception table offset in sortkey table */
DWORD ling_except; /* exception table offset for linguistic casing */
DWORD casemap; /* linguistic casemap table offset */
};
#define FLAG_HAS_3_BYTE_WEIGHTS 0x01
#define FLAG_REVERSEDIACRITICS 0x10
#define FLAG_DOUBLECOMPRESSION 0x20
#define FLAG_INVERSECASING 0x40
static const struct sortguid *current_locale_sort;
static const GUID default_sort_guid = { 0x00000001, 0x57ee, 0x1e5c, { 0x00, 0xb4, 0xd0, 0x00, 0x0b, 0xb1, 0xe1, 0x1e }};
static struct
{
DWORD *keys; /* sortkey table, indexed by char */
USHORT *casemap; /* casemap table, in l_intl.nls format */
WORD *ctypes; /* CT_CTYPE1,2,3 values */
BYTE *ctype_idx; /* index to map char to ctypes array entry */
DWORD *keys; /* sortkey table, indexed by char */
USHORT *casemap; /* casemap table, in l_intl.nls format */
WORD *ctypes; /* CT_CTYPE1,2,3 values */
BYTE *ctype_idx; /* index to map char to ctypes array entry */
DWORD version; /* NLS version */
DWORD guid_count; /* number of sort GUIDs */
struct sortguid *guids; /* table of sort GUIDs */
} sort;
static CRITICAL_SECTION locale_section;
@ -594,18 +616,82 @@ static CRITICAL_SECTION_DEBUG critsect_debug =
static CRITICAL_SECTION locale_section = { &critsect_debug, -1, 0, 0, 0, 0 };
/***********************************************************************
* init_sortkeys
*/
static void init_sortkeys( DWORD *ptr )
{
WORD *ctype;
DWORD *table;
sort.keys = (DWORD *)((char *)ptr + ptr[0]);
sort.casemap = (USHORT *)((char *)ptr + ptr[1]);
ctype = (WORD *)((char *)ptr + ptr[2]);
sort.ctypes = ctype + 2;
sort.keys = (DWORD *)((char *)ptr + ptr[0]);
sort.casemap = (USHORT *)((char *)ptr + ptr[1]);
ctype = (WORD *)((char *)ptr + ptr[2]);
sort.ctypes = ctype + 2;
sort.ctype_idx = (BYTE *)ctype + ctype[1] + 2;
table = (DWORD *)((char *)ptr + ptr[3]);
sort.version = table[0];
sort.guid_count = table[1];
sort.guids = (struct sortguid *)(table + 2);
}
static const struct sortguid *find_sortguid( const GUID *guid )
{
int pos, ret, min = 0, max = sort.guid_count - 1;
while (min <= max)
{
pos = (min + max) / 2;
ret = memcmp( guid, &sort.guids[pos].id, sizeof(*guid) );
if (!ret) return &sort.guids[pos];
if (ret > 0) min = pos + 1;
else max = pos - 1;
}
ERR( "no sort found for %s\n", debugstr_guid( guid ));
return NULL;
}
static const struct sortguid *get_language_sort( const WCHAR *locale )
{
WCHAR *p, *end, buffer[LOCALE_NAME_MAX_LENGTH], guidstr[39];
const struct sortguid *ret;
UNICODE_STRING str;
GUID guid;
HKEY key;
DWORD size, type;
if (locale == LOCALE_NAME_USER_DEFAULT)
{
if (current_locale_sort) return current_locale_sort;
GetUserDefaultLocaleName( buffer, ARRAY_SIZE( buffer ));
}
else lstrcpynW( buffer, locale, LOCALE_NAME_MAX_LENGTH );
if (buffer[0] && !RegOpenKeyExW( nls_key, L"Sorting\\Ids", 0, KEY_READ, &key ))
{
for (;;)
{
size = sizeof(guidstr);
if (!RegQueryValueExW( key, buffer, NULL, &type, (BYTE *)guidstr, &size ) && type == REG_SZ)
{
RtlInitUnicodeString( &str, guidstr );
if (!RtlGUIDFromString( &str, &guid ))
{
ret = find_sortguid( &guid );
goto done;
}
break;
}
for (p = end = buffer; *p; p++) if (*p == '-' || *p == '_') end = p;
if (end == buffer) break;
*end = 0;
}
}
ret = find_sortguid( &default_sort_guid );
done:
RegCloseKey( key );
return ret;
}
@ -648,6 +734,8 @@ void init_locale(void)
RtlInitNlsTables( ansi_ptr, oem_ptr, sort.casemap, &nls_info );
RtlResetRtlTranslations( &nls_info );
current_locale_sort = get_language_sort( LOCALE_NAME_USER_DEFAULT );
RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Nls",
0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL );
RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
@ -4737,6 +4825,7 @@ INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, co
WCHAR *dst, int dstlen, NLSVERSIONINFO *version,
void *reserved, LPARAM handle )
{
const struct sortguid *sortid;
LPWSTR dst_ptr;
INT len;
@ -4767,6 +4856,13 @@ INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, co
if (!dstlen) dst = NULL;
if (!(sortid = get_language_sort( locale )))
{
FIXME( "unknown locale %s\n", debugstr_w(locale) );
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (flags & LCMAP_SORTKEY)
{
INT ret;
@ -4910,7 +5006,8 @@ INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, co
if (flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))
{
USHORT *table = (flags & LCMAP_LOWERCASE) ? nls_info.LowerCaseTable : nls_info.UpperCaseTable;
const USHORT *table = sort.casemap + (flags & LCMAP_LINGUISTIC_CASING ? sortid->casemap : 0);
table = table + 2 + (flags & LCMAP_LOWERCASE ? table[1] : 0);
for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--, len--)
*dst_ptr++ = casemap( table, *src );
}