oleaut32: use the locale settings for digit group sizes

Damjan Jovanovic damjan.jov at gmail.com
Mon Jul 4 22:05:55 CDT 2011


Changelog:
* oleaut32: use the locale settings for digit group sizes

Definitely fixes digit grouping in Indic locales and might close
#25263. Either way it's one step closer to world domination ;-).

Damjan Jovanovic
-------------- next part --------------
diff --git a/dlls/oleaut32/tests/varformat.c b/dlls/oleaut32/tests/varformat.c
index ae74429..2c75868 100644
--- a/dlls/oleaut32/tests/varformat.c
+++ b/dlls/oleaut32/tests/varformat.c
@@ -249,6 +249,7 @@ static void test_VarFormat(void)
   int fd = 0, fw = 0;
   ULONG flags = 0;
   BSTR bstrin, out = NULL;
+  WCHAR origSGrouping[10];
   HRESULT hres;
 
   CHECKPTR(VarFormat);
@@ -420,6 +421,14 @@ static void test_VarFormat(void)
   VARFMT(VT_R8,V_R8,1.00,"#,##0.00",S_OK,"1.00");
   VARFMT(VT_R8,V_R8,0.0995,"#.###",S_OK,".1");
 
+  if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, origSGrouping, sizeof(origSGrouping)/sizeof(WCHAR)))
+  {
+    SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, "3;2;0");
+    VARFMT(VT_I4,V_I4,123456789,"#,#",S_OK,"12,34,56,789");
+    SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, origSGrouping);
+  }
+  else
+    skip("getting LOCALE_SGROUPING failed, skipping test\n");
 
   /* 'out' is not cleared */
   out = (BSTR)0x1;
diff --git a/dlls/oleaut32/varformat.c b/dlls/oleaut32/varformat.c
index 351529c..dbf0417 100644
--- a/dlls/oleaut32/varformat.c
+++ b/dlls/oleaut32/varformat.c
@@ -1184,6 +1184,70 @@ HRESULT WINAPI VarTokenizeFormatString(LPOLESTR lpszFormat, LPBYTE rgbTok,
 #define NUM_WRITE_ON   0x02 /* Started to write the number */
 #define NUM_WROTE_SIGN 0x04 /* Written the negative sign */
 
+struct grouping_state
+{
+  int digitGroupCount;
+  int digitGroupOffsets[10];
+  BOOL recurs;
+  int recurringOffset;
+  int recurringLength;
+};
+
+static void parse_sgrouping(WCHAR *sgrouping, struct grouping_state *state)
+{
+  WCHAR *prev;
+  WCHAR *digit;
+  int total = 0;
+  int secondLastGroup = 0;
+  int lastGroup;
+
+  prev = digit = sgrouping;
+  state->digitGroupCount = 0;
+  while (*digit)
+  {
+    if (*digit == ';')
+    {
+      *digit = 0;
+      secondLastGroup = atoiW(prev);
+      total += secondLastGroup;
+      state->digitGroupOffsets[state->digitGroupCount++] = total;
+      prev = ++digit;
+    }
+    else
+      digit++;
+  }
+  lastGroup = atoiW(prev);
+  if (lastGroup == 0)
+  {
+    state->recurs = TRUE;
+    state->recurringOffset = total + secondLastGroup;
+    state->recurringLength = secondLastGroup;
+  }
+  else
+  {
+    state->recurs = FALSE;
+    state->digitGroupOffsets[state->digitGroupCount++] = total + lastGroup;
+  }
+}
+
+static BOOL needs_separator(struct grouping_state *grouping, int position)
+{
+  if (position <= 0)
+    return FALSE;
+  if (grouping->digitGroupCount > 0 &&
+      grouping->digitGroupOffsets[grouping->digitGroupCount - 1] == position)
+  {
+    grouping->digitGroupCount--;
+    return TRUE;
+  }
+  if (grouping->recurs && position >= grouping->recurringOffset)
+  {
+    if ((position - grouping->recurringOffset) % grouping->recurringLength == 0)
+      return TRUE;
+  }
+  return FALSE;
+}
+
 /* Format a variant using a number format */
 static HRESULT VARIANT_FormatNumber(LPVARIANT pVarIn, LPOLESTR lpszFormat,
                                     LPBYTE rgbTok, ULONG dwFlags,
@@ -1194,6 +1258,7 @@ static HRESULT VARIANT_FormatNumber(LPVARIANT pVarIn, LPOLESTR lpszFormat,
   int have_int, need_int = 0, have_frac, need_frac, exponent = 0, pad = 0;
   WCHAR buff[256], *pBuff = buff;
   WCHAR thousandSeparator[32];
+  struct grouping_state digitGrouping;
   VARIANT vString, vBool;
   DWORD dwState = 0;
   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
@@ -1330,12 +1395,21 @@ static HRESULT VARIANT_FormatNumber(LPVARIANT pVarIn, LPOLESTR lpszFormat,
 
   if (numHeader->flags & FMT_FLAG_THOUSANDS)
   {
+    WCHAR sgrouping[10];
     if (!GetLocaleInfoW(lcid, LOCALE_STHOUSAND, thousandSeparator,
                         sizeof(thousandSeparator)/sizeof(WCHAR)))
     {
       thousandSeparator[0] = ',';
       thousandSeparator[1] = 0;
     }
+    if (GetLocaleInfoW(lcid, LOCALE_SGROUPING, sgrouping,
+                        sizeof(sgrouping)/sizeof(WCHAR)))
+      parse_sgrouping(sgrouping, &digitGrouping);
+    else
+    {
+      digitGrouping.digitGroupCount = 0;
+      digitGrouping.recurs = FALSE;
+    }
   }
 
   pToken = (const BYTE*)numHeader + sizeof(FMT_NUMBER_HEADER);
@@ -1513,7 +1587,7 @@ VARIANT_FormatNumber_Bool:
           {
             *pBuff++ = '0';
             if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
-                position > 1 && (--position % 3) == 0)
+                needs_separator(&digitGrouping, --position))
             {
               int k;
               TRACE("write thousand separator\n");
@@ -1534,7 +1608,7 @@ VARIANT_FormatNumber_Bool:
             dwState |= NUM_WRITE_ON;
             *pBuff++ = '0' + *prgbDig++;
             if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
-                position > 1 && (--position % 3) == 0)
+                needs_separator(&digitGrouping, --position))
             {
               int k;
               TRACE("write thousand separator\n");
@@ -1550,7 +1624,7 @@ VARIANT_FormatNumber_Bool:
         {
           *pBuff++ = '0';
           if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
-              position > 1 && (--position % 3) == 0)
+              needs_separator(&digitGrouping, --position))
           {
             int k;
             TRACE("write thousand separator\n");


More information about the wine-patches mailing list