Commit 8d02e4e2 authored by Gabriel Ivăncescu's avatar Gabriel Ivăncescu Committed by Alexandre Julliard

kernelbase: Fix grouping repeat for number formatting.

Fixes a regression introduced by 56099a31. LOCALE_SGROUPING's string and the `Grouping` field in NUMBERFMTW are confusingly different. The former, which is what is fixed here, treats a '0' as a repeat of the previous grouping. But a 0 at the end of the `Grouping` field prevents it from repeating (it repeats by default otherwise) so it's the opposite. Note that without a '0' in the LOCALE_SGROUPING string, it shouldn't even repeat in the first place. Signed-off-by: 's avatarGabriel Ivăncescu <gabrielopcode@gmail.com>
parent 2d3516cd
......@@ -1404,6 +1404,7 @@ static void test_GetNumberFormatA(void)
static char szComma[] = { ',', '\0' };
int ret;
LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
WCHAR grouping[32], t1000[8], dec[8], frac[8], lzero[8];
char buffer[BUFFER_SIZE];
NUMBERFMTA format;
......@@ -1522,6 +1523,22 @@ static void test_GetNumberFormatA(void)
ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
expect_str(ret, buffer, "1234,567,89.0");
format.Grouping = 203;
ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
expect_str(ret, buffer, "1,234,567,,89.0");
format.Grouping = 2030;
ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
expect_str(ret, buffer, "1234,567,,89.0");
format.Grouping = 2003;
ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
expect_str(ret, buffer, "1,234,567,,,89.0");
format.Grouping = 1200;
ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
expect_str(ret, buffer, "123456,,78,9.0");
/* Grouping of a negative number */
format.NegativeOrder = NEG_LEFT;
format.Grouping = 3;
......@@ -1563,6 +1580,67 @@ static void test_GetNumberFormatA(void)
ret = GetNumberFormatA(lcid, NUO, "-12345", NULL, buffer, ARRAY_SIZE(buffer));
expect_str(ret, buffer, "-12\xa0\x33\x34\x35,00"); /* Non breaking space */
}
/* Test the actual LOCALE_SGROUPING string, the rules for repeats are opposite */
if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, ARRAY_SIZE(grouping)) &&
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, t1000, ARRAY_SIZE(t1000)) &&
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, dec, ARRAY_SIZE(dec)) &&
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, frac, ARRAY_SIZE(frac)) &&
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, lzero, ARRAY_SIZE(lzero)))
{
static const struct
{
const char *grouping;
const char *expected;
} tests[] = {
{ "3;0", "1,234,567,890.54321" },
{ "2;3", "12345,678,90.54321" },
{ "1", "123456789,0.54321" },
{ "1;0", "1,2,3,4,5,6,7,8,9,0.54321" },
{ "1;0;3", "123456,789,,0.54321" },
{ "0", "1234567890.54321" },
{ "0;0", "1234567890.54321" },
{ "0;1", "123456789,0.54321" },
{ "0;0;0", "1234567890.54321" },
{ "0;1;0", "1,2,3,4,5,6,7,8,9,0.54321" },
{ "2;0;0", "12345678,90.54321" },
{ "2;0;0;0", "12345678,,90.54321" },
{ "2;0;0;0;0", "12345678,,,90.54321" },
{ "2;0;0;1;0", "1,2,3,4,5,6,7,8,,,90.54321" },
{ "1;3;2", "1234,56,789,0.54321" },
{ "1;3;2;0", "12,34,56,789,0.54321" },
{ "3;1;1;2;0", "1,23,45,6,7,890.54321" },
{ "6;1", "123,4,567890.54321" },
};
unsigned i;
SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, ",");
SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, ".");
SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, "5");
SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILZERO, "0");
for (i = 0; i < ARRAY_SIZE(tests); i++)
{
SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, tests[i].grouping);
SetLastError(0xdeadbeef);
ret = GetNumberFormatA(LOCALE_USER_DEFAULT, 0, "1234567890.54321", NULL, buffer, ARRAY_SIZE(buffer));
if (ret)
{
ok(GetLastError() == 0xdeadbeef, "[%u] unexpected error %lu\n", i, GetLastError());
ok(ret == strlen(tests[i].expected) + 1, "[%u] unexpected ret %d\n", i, ret);
ok(!strcmp(buffer, tests[i].expected), "[%u] unexpected string %s\n", i, buffer);
}
else
ok(0, "[%u] expected success, got error %ld\n", i, GetLastError());
}
/* Restore */
ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping), "Restoring SGROUPING failed\n");
ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, t1000), "Restoring STHOUSAND failed\n");
ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, dec), "Restoring SDECIMAL failed\n");
ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, frac), "Restoring IDIGITS failed\n");
ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, lzero), "Restoring ILZERO failed\n");
}
}
static void test_GetNumberFormatEx(void)
......
......@@ -7245,8 +7245,20 @@ BOOL WINAPI SetUserGeoName(PWSTR geo_name)
static void grouping_to_string( UINT grouping, WCHAR *buffer )
{
UINT last_digit = grouping % 10;
WCHAR tmp[10], *p = tmp;
/* The string is confusingly different when it comes to repetitions (trailing zeros). For a string,
* a 0 signals that the format needs to be repeated, which is the opposite of the grouping integer. */
if (last_digit == 0)
{
grouping /= 10;
/* Special case: two or more trailing zeros result in zero-sided groupings, with no repeats */
if (grouping % 10 == 0)
last_digit = ~0;
}
while (grouping)
{
*p++ = '0' + grouping % 10;
......@@ -7257,6 +7269,17 @@ static void grouping_to_string( UINT grouping, WCHAR *buffer )
*buffer++ = *(--p);
if (p > tmp) *buffer++ = ';';
}
if (last_digit != 0)
{
*buffer++ = ';';
*buffer++ = '0';
if (last_digit == ~0)
{
/* Add another trailing zero due to the weird way trailing zeros work in grouping string */
*buffer++ = ';';
*buffer++ = '0';
}
}
*buffer = 0;
}
......@@ -7272,9 +7295,9 @@ static WCHAR *prepend_str( WCHAR *end, const WCHAR *str )
static WCHAR *format_number( WCHAR *end, const WCHAR *value, const WCHAR *decimal_sep,
const WCHAR *thousand_sep, const WCHAR *grouping, UINT digits, BOOL lzero )
{
BOOL round = FALSE, repeat = FALSE;
UINT i, len = 0, prev = ~0;
const WCHAR *frac = NULL;
BOOL round = FALSE;
UINT i, len = 0;
*(--end) = 0;
......@@ -7327,9 +7350,43 @@ static WCHAR *format_number( WCHAR *end, const WCHAR *value, const WCHAR *decima
}
if (len) lzero = FALSE;
/* leading 0s are ignored */
while (grouping[0] == '0' && grouping[1] == ';')
grouping += 2;
while (len)
{
UINT limit = *grouping == '0' ? ~0u : *grouping - '0';
UINT limit = prev;
if (!repeat)
{
limit = *grouping - '0';
if (grouping[1] == ';')
{
grouping += 2;
if (limit)
prev = limit;
else
{
/* Trailing 0;0 is a special case */
prev = ~0;
if (grouping[0] == '0' && grouping[1] != ';')
{
repeat = TRUE;
limit = prev;
}
}
}
else
{
repeat = TRUE;
if (!limit)
limit = prev;
else
prev = ~0;
}
}
while (len && limit--)
{
WCHAR ch = value[--len];
......@@ -7345,7 +7402,6 @@ static WCHAR *format_number( WCHAR *end, const WCHAR *value, const WCHAR *decima
*(--end) = ch;
}
if (len) end = prepend_str( end, thousand_sep );
if (grouping[1] == ';') grouping += 2;
}
if (round) *(--end) = '1';
else if (lzero) *(--end) = '0';
......@@ -7356,7 +7412,7 @@ static WCHAR *format_number( WCHAR *end, const WCHAR *value, const WCHAR *decima
static int get_number_format( const NLS_LOCALE_DATA *locale, DWORD flags, const WCHAR *value,
const NUMBERFMTW *format, WCHAR *buffer, int len )
{
WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_neg[5], grouping[20], output[256];
WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_neg[5], grouping[24], output[256];
const WCHAR *decimal_sep = fmt_decimal, *thousand_sep = fmt_thousand;
DWORD digits, lzero, order;
int ret = 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment