diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c index 3b485aeca09..a72095a5dd7 100644 --- a/dlls/kernel32/locale.c +++ b/dlls/kernel32/locale.c @@ -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.@) * diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 5ae7bd492b7..ee38dc36acd 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -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;