kernel32: Reimplement FoldStringW() to support composition.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2019-11-28 09:04:33 +01:00
parent 95c9dee30a
commit e89bbd5244
2 changed files with 160 additions and 50 deletions

View File

@ -2125,8 +2125,7 @@ INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
return 0;
}
srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
src, srclen, NULL, 0);
srclenW = MultiByteToWideChar(CP_ACP, 0, src, srclen, NULL, 0);
srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
if (!srcW)
@ -2135,10 +2134,7 @@ INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
goto FoldStringA_exit;
}
MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
src, srclen, srcW, srclenW);
dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
MultiByteToWideChar(CP_ACP, 0, src, srclen, srcW, srclenW);
ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
if (ret && dstlen)
@ -2166,6 +2162,96 @@ FoldStringA_exit:
return ret;
}
/* Unicode expanded ligatures */
static const WCHAR ligatures[][5] =
{
{ 0x00c6, 'A','E',0 },
{ 0x00de, 'T','H',0 },
{ 0x00df, 's','s',0 },
{ 0x00e6, 'a','e',0 },
{ 0x00fe, 't','h',0 },
{ 0x0132, 'I','J',0 },
{ 0x0133, 'i','j',0 },
{ 0x0152, 'O','E',0 },
{ 0x0153, 'o','e',0 },
{ 0x01c4, 'D',0x017d,0 },
{ 0x01c5, 'D',0x017e,0 },
{ 0x01c6, 'd',0x017e,0 },
{ 0x01c7, 'L','J',0 },
{ 0x01c8, 'L','j',0 },
{ 0x01c9, 'l','j',0 },
{ 0x01ca, 'N','J',0 },
{ 0x01cb, 'N','j',0 },
{ 0x01cc, 'n','j',0 },
{ 0x01e2, 0x0100,0x0112,0 },
{ 0x01e3, 0x0101,0x0113,0 },
{ 0x01f1, 'D','Z',0 },
{ 0x01f2, 'D','z',0 },
{ 0x01f3, 'd','z',0 },
{ 0x01fc, 0x00c1,0x00c9,0 },
{ 0x01fd, 0x00e1,0x00e9,0 },
{ 0x05f0, 0x05d5,0x05d5,0 },
{ 0x05f1, 0x05d5,0x05d9,0 },
{ 0x05f2, 0x05d9,0x05d9,0 },
{ 0xfb00, 'f','f',0 },
{ 0xfb01, 'f','i',0 },
{ 0xfb02, 'f','l',0 },
{ 0xfb03, 'f','f','i',0 },
{ 0xfb04, 'f','f','l',0 },
{ 0xfb05, 0x017f,'t',0 },
{ 0xfb06, 's','t',0 },
};
static const WCHAR *get_ligature( WCHAR wc )
{
int low = 0, high = ARRAY_SIZE( ligatures ) -1;
while (low <= high)
{
int pos = (low + high) / 2;
if (ligatures[pos][0] < wc) low = pos + 1;
else if (ligatures[pos][0] > wc) high = pos - 1;
else return ligatures[pos] + 1;
}
return NULL;
}
static int expand_ligatures( const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
{
int i, len, pos = 0;
const WCHAR *expand;
for (i = 0; i < srclen; i++)
{
if (!(expand = get_ligature( src[i] )))
{
expand = src + i;
len = 1;
}
else len = lstrlenW( expand );
if (dstlen)
{
if (pos + len > dstlen) break;
memcpy( dst + pos, expand, len * sizeof(WCHAR) );
}
pos += len;
}
return pos;
}
static int fold_digits( const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
{
extern const WCHAR wine_digitmap[] DECLSPEC_HIDDEN;
int i;
if (!dstlen) return srclen;
if (srclen > dstlen) return 0;
for (i = 0; i < srclen; i++)
dst[i] = src[i] + wine_digitmap[wine_digitmap[src[i] >> 8] + (src[i] & 0xff)];
return srclen;
}
/*************************************************************************
* FoldStringW (KERNEL32.@)
*
@ -2174,33 +2260,73 @@ FoldStringA_exit:
INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
LPWSTR dst, INT dstlen)
{
WCHAR *tmp;
int ret;
switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
{
case 0:
if (dwFlags)
break;
/* Fall through for dwFlags == 0 */
case MAP_PRECOMPOSED|MAP_COMPOSITE:
case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
SetLastError(ERROR_INVALID_FLAGS);
return 0;
}
if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
{
SetLastError(ERROR_INVALID_PARAMETER);
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (srclen == -1) srclen = lstrlenW(src) + 1;
ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
if (!ret)
SetLastError(ERROR_INSUFFICIENT_BUFFER);
switch (dwFlags)
{
case MAP_PRECOMPOSED:
return NormalizeString( NormalizationC, src, srclen, dst, dstlen );
case MAP_FOLDCZONE:
case MAP_PRECOMPOSED | MAP_FOLDCZONE:
return NormalizeString( NormalizationKC, src, srclen, dst, dstlen );
case MAP_COMPOSITE:
return NormalizeString( NormalizationD, src, srclen, dst, dstlen );
case MAP_COMPOSITE | MAP_FOLDCZONE:
return NormalizeString( NormalizationKD, src, srclen, dst, dstlen );
case MAP_FOLDDIGITS:
return fold_digits( src, srclen, dst, dstlen );
case MAP_EXPAND_LIGATURES:
case MAP_EXPAND_LIGATURES | MAP_FOLDCZONE:
return expand_ligatures( src, srclen, dst, dstlen );
case MAP_FOLDDIGITS | MAP_PRECOMPOSED:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = NormalizeString( NormalizationC, tmp, srclen, dst, dstlen );
break;
case MAP_FOLDDIGITS | MAP_FOLDCZONE:
case MAP_FOLDDIGITS | MAP_PRECOMPOSED | MAP_FOLDCZONE:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = NormalizeString( NormalizationKC, tmp, srclen, dst, dstlen );
break;
case MAP_FOLDDIGITS | MAP_COMPOSITE:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = NormalizeString( NormalizationD, tmp, srclen, dst, dstlen );
break;
case MAP_FOLDDIGITS | MAP_COMPOSITE | MAP_FOLDCZONE:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = NormalizeString( NormalizationKD, tmp, srclen, dst, dstlen );
break;
case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS:
case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS | MAP_FOLDCZONE:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = expand_ligatures( tmp, srclen, dst, dstlen );
break;
default:
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (!tmp)
{
SetLastError( ERROR_OUTOFMEMORY );
return 0;
}
RtlFreeHeap( GetProcessHeap(), 0, tmp );
return ret;
}
/******************************************************************************
* CompareStringW (KERNEL32.@)
*

View File

@ -3429,23 +3429,15 @@ static void test_FoldStringW(void)
static const WCHAR foldczone_src[] =
{
'W', 'i', 'n', 'e', 0x0348, 0x0551, 0x1323, 0x280d,
0xff37, 0xff49, 0xff4e, 0xff45, '\0'
0xff37, 0xff49, 0xff4e, 0xff45, 0x3c5, 0x308, 0x6a, 0x30c, 0xa0, 0xaa, 0
};
static const WCHAR foldczone_dst[] =
{
'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e','\0'
'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e',0x3cb,0x1f0,' ','a',0
};
static const WCHAR foldczone_todo_src[] =
static const WCHAR foldczone_broken_dst[] =
{
0x3c5,0x308,0x6a,0x30c,0xa0,0xaa,0
};
static const WCHAR foldczone_todo_dst[] =
{
0x3cb,0x1f0,' ','a',0
};
static const WCHAR foldczone_todo_broken_dst[] =
{
0x3cb,0x1f0,0xa0,0xaa,0
'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e',0x3cb,0x1f0,0xa0,0xaa,0
};
static const WCHAR ligatures_src[] =
{
@ -3589,15 +3581,10 @@ static void test_FoldStringW(void)
SetLastError(0);
ret = pFoldStringW(MAP_FOLDCZONE, foldczone_src, -1, dst, 256);
ok(ret == ARRAY_SIZE(foldczone_dst), "Got %d, error %d\n", ret, GetLastError());
ok(!memcmp(dst, foldczone_dst, sizeof(foldczone_dst)),
ok(!memcmp(dst, foldczone_dst, sizeof(foldczone_dst))
|| broken(!memcmp(dst, foldczone_broken_dst, sizeof(foldczone_broken_dst))),
"MAP_FOLDCZONE: Expanded incorrectly\n");
ret = pFoldStringW(MAP_FOLDCZONE|MAP_PRECOMPOSED, foldczone_todo_src, -1, dst, 256);
todo_wine ok(ret == ARRAY_SIZE(foldczone_todo_dst), "Got %d, error %d\n", ret, GetLastError());
todo_wine ok(!memcmp(dst, foldczone_todo_dst, sizeof(foldczone_todo_dst))
|| broken(!memcmp(dst, foldczone_todo_broken_dst, sizeof(foldczone_todo_broken_dst))),
"MAP_FOLDCZONE: Expanded incorrectly (%s)\n", wine_dbgstr_w(dst));
/* MAP_EXPAND_LIGATURES */
SetLastError(0);
ret = pFoldStringW(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
@ -4439,7 +4426,6 @@ static void test_IdnToNameprepUnicode(void)
const WCHAR out[64];
DWORD flags;
DWORD err;
DWORD todo;
} test_data[] = {
{
5, {'t','e','s','t',0},
@ -4481,20 +4467,20 @@ static void test_IdnToNameprepUnicode(void)
0, 0, {0},
IDN_USE_STD3_ASCII_RULES, ERROR_INVALID_NAME
},
{ /* FoldString is not working as expected when MAP_FOLDCZONE is specified (composition+compatibility) */
{
10, {'T',0xdf,0x130,0x143,0x37a,0x6a,0x30c,' ',0xaa,0},
12, 12, {'t','s','s','i',0x307,0x144,' ',0x3b9,0x1f0,' ','a',0},
0, 0xdeadbeef, TRUE
0, 0xdeadbeef
},
{
11, {'t',0xad,0x34f,0x1806,0x180b,0x180c,0x180d,0x200b,0x200c,0x200d,0},
2, 0, {'t',0},
0, 0xdeadbeef
},
{ /* Another example of incorrectly working FoldString (composition) */
{
2, {0x3b0, 0},
2, 2, {0x3b0, 0},
0, 0xdeadbeef, TRUE
0, 0xdeadbeef,
},
{
2, {0x221, 0},
@ -4580,9 +4566,7 @@ static void test_IdnToNameprepUnicode(void)
buf, ARRAY_SIZE(buf));
err = GetLastError();
todo_wine_if (test_data[i].todo)
ok(ret == test_data[i].ret ||
broken(ret == test_data[i].broken_ret), "%d) ret = %d\n", i, ret);
ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%d: ret = %d\n", i, ret);
if(ret != test_data[i].ret)
continue;