1// Copyright 2014 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include <algorithm>
8
9#include "core/include/fxcrt/fx_xml.h"
10#include "xfa/src/fgas/src/fgas_base.h"
11#include "fx_localeimp.h"
12
13#define FX_LOCALECATEGORY_DateHash 0xbde9abde
14#define FX_LOCALECATEGORY_TimeHash 0x2d71b00f
15#define FX_LOCALECATEGORY_DateTimeHash 0x158c72ed
16#define FX_LOCALECATEGORY_NumHash 0x0b4ff870
17#define FX_LOCALECATEGORY_TextHash 0x2d08af85
18#define FX_LOCALECATEGORY_ZeroHash 0x568cb500
19#define FX_LOCALECATEGORY_NullHash 0x052931bb
20typedef struct _FX_LOCALESUBCATEGORYINFO {
21  uint32_t uHash;
22  const FX_WCHAR* pName;
23  int32_t eSubCategory;
24} FX_LOCALESUBCATEGORYINFO, *FX_LPLOCALESUBCATEGORYINFO;
25typedef FX_LOCALESUBCATEGORYINFO const* FX_LPCLOCALESUBCATEGORYINFO;
26const static FX_LOCALESUBCATEGORYINFO g_FXLocaleDateTimeSubCatData[] = {
27    {0x14da2125, L"default", FX_LOCALEDATETIMESUBCATEGORY_Default},
28    {0x9041d4b0, L"short", FX_LOCALEDATETIMESUBCATEGORY_Short},
29    {0xa084a381, L"medium", FX_LOCALEDATETIMESUBCATEGORY_Medium},
30    {0xcdce56b3, L"full", FX_LOCALEDATETIMESUBCATEGORY_Full},
31    {0xf6b4afb0, L"long", FX_LOCALEDATETIMESUBCATEGORY_Long},
32};
33const static int32_t g_iFXLocaleDateTimeSubCatCount =
34    sizeof(g_FXLocaleDateTimeSubCatData) / sizeof(FX_LOCALESUBCATEGORYINFO);
35const static FX_LOCALESUBCATEGORYINFO g_FXLocaleNumSubCatData[] = {
36    {0x46f95531, L"percent", FX_LOCALENUMPATTERN_Percent},
37    {0x4c4e8acb, L"currency", FX_LOCALENUMPATTERN_Currency},
38    {0x54034c2f, L"decimal", FX_LOCALENUMPATTERN_Decimal},
39    {0x7568e6ae, L"integer", FX_LOCALENUMPATTERN_Integer},
40};
41const static int32_t g_iFXLocaleNumSubCatCount =
42    sizeof(g_FXLocaleNumSubCatData) / sizeof(FX_LOCALESUBCATEGORYINFO);
43typedef struct _FX_LOCALETIMEZONEINFO {
44  FX_DWORD uHash;
45  int16_t iHour;
46  int16_t iMinute;
47} FX_LOCALETIMEZONEINFO, *FX_LPLOCALETIMEZONEINFO;
48typedef FX_LOCALETIMEZONEINFO const* FX_LPCLOCALETIMEZONEINFO;
49const static FX_LOCALETIMEZONEINFO g_FXLocaleTimeZoneData[] = {
50    {FXBSTR_ID(0, 'C', 'D', 'T'), -5, 0}, {FXBSTR_ID(0, 'C', 'S', 'T'), -6, 0},
51    {FXBSTR_ID(0, 'E', 'D', 'T'), -4, 0}, {FXBSTR_ID(0, 'E', 'S', 'T'), -5, 0},
52    {FXBSTR_ID(0, 'M', 'D', 'T'), -6, 0}, {FXBSTR_ID(0, 'M', 'S', 'T'), -7, 0},
53    {FXBSTR_ID(0, 'P', 'D', 'T'), -7, 0}, {FXBSTR_ID(0, 'P', 'S', 'T'), -8, 0},
54};
55const static int32_t g_iFXLocaleTimeZoneCount =
56    sizeof(g_FXLocaleTimeZoneData) / sizeof(FX_LOCALETIMEZONEINFO);
57const static CFX_WideStringC gs_wsTextSymbols = FX_WSTRC(L"AXO09");
58const static CFX_WideStringC gs_wsTimeSymbols = FX_WSTRC(L"hHkKMSFAzZ");
59const static CFX_WideStringC gs_wsDateSymbols = FX_WSTRC(L"DJMEeGgYwW");
60const static CFX_WideStringC gs_wsConstChars = FX_WSTRC(L",-:/. ");
61static FX_STRSIZE FX_Local_Find(const CFX_WideStringC& wsSymbols,
62                                FX_WCHAR ch,
63                                FX_STRSIZE nStart = 0) {
64  FX_STRSIZE nLength = wsSymbols.GetLength();
65  if (nLength < 1 || nStart > nLength) {
66    return -1;
67  }
68  const FX_WCHAR* lpsz =
69      (const FX_WCHAR*)FXSYS_wcschr(wsSymbols.GetPtr() + nStart, ch);
70  return (lpsz == NULL) ? -1 : (FX_STRSIZE)(lpsz - wsSymbols.GetPtr());
71}
72static const FX_WCHAR* const gs_LocalNumberSymbols[] = {
73    L"decimal", L"grouping",       L"percent",      L"minus",
74    L"zero",    L"currencySymbol", L"currencyName",
75};
76IFX_Locale* IFX_Locale::Create(CXML_Element* pLocaleData) {
77  return new CFX_Locale(pLocaleData);
78}
79CFX_Locale::CFX_Locale(CXML_Element* pLocaleData) {
80  m_pElement = pLocaleData;
81}
82CFX_Locale::~CFX_Locale() {}
83CFX_WideString CFX_Locale::GetName() {
84  return CFX_WideString();
85}
86static CFX_WideString FX_GetXMLContent(const CFX_ByteStringC& bsSpace,
87                                       CXML_Element* pxmlElement,
88                                       const CFX_ByteStringC& bsTag,
89                                       const CFX_WideStringC& wsName) {
90  CXML_Element* pDatePattern = NULL;
91  int32_t nCount = pxmlElement->CountElements(bsSpace, bsTag);
92  int32_t i = 0;
93  for (; i < nCount; i++) {
94    pDatePattern = pxmlElement->GetElement(bsSpace, bsTag, i);
95    if (pDatePattern->GetAttrValue("name") == wsName) {
96      break;
97    }
98  }
99  if (i < nCount && pDatePattern) {
100    return pDatePattern->GetContent(0);
101  }
102  return L"";
103}
104void CFX_Locale::GetNumbericSymbol(FX_LOCALENUMSYMBOL eType,
105                                   CFX_WideString& wsNumSymbol) const {
106  if (!m_pElement) {
107    return;
108  }
109  CFX_ByteString bsSpace;
110  CFX_WideString wsName = gs_LocalNumberSymbols[eType];
111  CXML_Element* pNumberSymbols =
112      m_pElement->GetElement(bsSpace, "numberSymbols");
113  if (!pNumberSymbols) {
114    return;
115  }
116  wsNumSymbol =
117      FX_GetXMLContent(bsSpace, pNumberSymbols, "numberSymbol", wsName);
118}
119void CFX_Locale::GetDateTimeSymbols(CFX_WideString& wsDtSymbol) const {
120  if (!m_pElement) {
121    return;
122  }
123  CFX_ByteString bsSpace;
124  CXML_Element* pNumberSymbols =
125      m_pElement->GetElement(bsSpace, "dateTimeSymbols");
126  if (!pNumberSymbols) {
127    return;
128  }
129  wsDtSymbol = pNumberSymbols->GetContent(0);
130}
131static void FX_GetCalendarSymbol(CXML_Element* pXmlElement,
132                                 const CFX_ByteString& symbol_type,
133                                 int32_t index,
134                                 FX_BOOL bAbbr,
135                                 CFX_WideString& wsName) {
136  CFX_ByteString bsSpace;
137  CFX_ByteString pstrSymbolNames = symbol_type + "Names";
138  CXML_Element* pChild = pXmlElement->GetElement(bsSpace, "calendarSymbols");
139  if (!pChild) {
140    return;
141  }
142  CXML_Element* pSymbolNames = pChild->GetElement(bsSpace, pstrSymbolNames);
143  if (!pSymbolNames) {
144    return;
145  }
146  if (pSymbolNames->GetAttrInteger("abbr") != bAbbr) {
147    pSymbolNames = pChild->GetElement(bsSpace, pstrSymbolNames, 1);
148  }
149  if (pSymbolNames && pSymbolNames->GetAttrInteger("abbr") == bAbbr) {
150    CXML_Element* pSymbolName =
151        pSymbolNames->GetElement(bsSpace, symbol_type, index);
152    if (pSymbolName) {
153      wsName = pSymbolName->GetContent(0);
154    }
155  }
156}
157void CFX_Locale::GetMonthName(int32_t nMonth,
158                              CFX_WideString& wsMonthName,
159                              FX_BOOL bAbbr) const {
160  if (!m_pElement) {
161    return;
162  }
163  FX_GetCalendarSymbol(m_pElement, "month", nMonth, bAbbr, wsMonthName);
164}
165void CFX_Locale::GetDayName(int32_t nWeek,
166                            CFX_WideString& wsDayName,
167                            FX_BOOL bAbbr) const {
168  if (!m_pElement) {
169    return;
170  }
171  FX_GetCalendarSymbol(m_pElement, "day", nWeek, bAbbr, wsDayName);
172}
173void CFX_Locale::GetMeridiemName(CFX_WideString& wsMeridiemName,
174                                 FX_BOOL bAM) const {
175  if (!m_pElement) {
176    return;
177  }
178  FX_GetCalendarSymbol(m_pElement, "meridiem", bAM ? 0 : 1, FALSE,
179                       wsMeridiemName);
180}
181static int32_t FX_ParseTimeZone(const FX_WCHAR* pStr,
182                                int32_t iLen,
183                                FX_TIMEZONE& tz) {
184  tz.tzHour = 0;
185  tz.tzMinute = 0;
186  if (iLen < 0) {
187    return 0;
188  }
189  int32_t iStart = 1;
190  int32_t iEnd = iStart + 2;
191  while (iStart < iLen && iStart < iEnd) {
192    tz.tzHour = tz.tzHour * 10 + pStr[iStart++] - '0';
193  }
194  if (iStart < iLen && pStr[iStart] == ':') {
195    iStart++;
196  }
197  iEnd = iStart + 2;
198  while (iStart < iLen && iStart < iEnd) {
199    tz.tzMinute = tz.tzMinute * 10 + pStr[iStart++] - '0';
200  }
201  if (pStr[0] == '-') {
202    tz.tzHour = -tz.tzHour;
203  }
204  return iStart;
205}
206void CFX_Locale::GetTimeZone(FX_TIMEZONE& tz) const {
207  tz.tzHour = 0;
208  tz.tzMinute = 0;
209  if (!m_pElement) {
210    return;
211  }
212  CXML_Element* pxmlTimeZone = m_pElement->GetElement("", "timeZone");
213  if (pxmlTimeZone) {
214    CFX_WideString wsTimeZone = pxmlTimeZone->GetContent(0);
215    FX_ParseTimeZone(wsTimeZone, wsTimeZone.GetLength(), tz);
216  }
217}
218void CFX_Locale::GetEraName(CFX_WideString& wsEraName, FX_BOOL bAD) const {
219  if (!m_pElement) {
220    return;
221  }
222  FX_GetCalendarSymbol(m_pElement, "era", bAD ? 0 : 1, FALSE, wsEraName);
223}
224static void FX_GetPattern(CXML_Element* pXmlElement,
225                          const CFX_ByteString& bsCategory,
226                          const CFX_WideString& wsSubCategory,
227                          CFX_WideString& wsPattern) {
228  CFX_ByteString bsSpace;
229  CXML_Element* pDatePatterns =
230      pXmlElement->GetElement(bsSpace, bsCategory + "s");
231  if (!pDatePatterns) {
232    return;
233  }
234  wsPattern =
235      FX_GetXMLContent(bsSpace, pDatePatterns, bsCategory, wsSubCategory);
236}
237static void FX_GetDateTimePattern(CXML_Element* pXmlElement,
238                                  const CFX_ByteString& bsCategory,
239                                  FX_LOCALEDATETIMESUBCATEGORY eType,
240                                  CFX_WideString& wsPattern) {
241  CFX_WideString wsType = g_FXLocaleDateTimeSubCatData[eType].pName;
242  FX_GetPattern(pXmlElement, bsCategory, wsType, wsPattern);
243}
244void CFX_Locale::GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY eType,
245                                CFX_WideString& wsPattern) const {
246  if (!m_pElement) {
247    return;
248  }
249  FX_GetDateTimePattern(m_pElement, "datePattern", eType, wsPattern);
250}
251void CFX_Locale::GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY eType,
252                                CFX_WideString& wsPattern) const {
253  if (!m_pElement) {
254    return;
255  }
256  FX_GetDateTimePattern(m_pElement, "timePattern", eType, wsPattern);
257}
258void CFX_Locale::GetNumPattern(FX_LOCALENUMSUBCATEGORY eType,
259                               CFX_WideString& wsPattern) const {
260  CFX_WideString wsType = g_FXLocaleNumSubCatData[eType].pName;
261  FX_GetPattern(m_pElement, "numberPattern", wsType, wsPattern);
262}
263static FX_BOOL FX_IsDigit(FX_WCHAR c) {
264  return c >= '0' && c <= '9';
265}
266static FX_BOOL FX_IsAlpha(FX_WCHAR c) {
267  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
268}
269static FX_BOOL FX_IsSpace(FX_WCHAR c) {
270  return (c == 0x20) || (c == 0x0d) || (c == 0x0a) || (c == 0x09);
271}
272static const FX_FLOAT gs_fraction_scales[] = {
273    0.1f,         0.01f,         0.001f,        0.0001f,
274    0.00001f,     0.000001f,     0.0000001f,    0.00000001f,
275    0.000000001f, 0.0000000001f, 0.00000000001f};
276static const int32_t gs_fraction_count =
277    sizeof(gs_fraction_scales) / sizeof(FX_FLOAT);
278class CFX_LCNumeric {
279 public:
280  CFX_LCNumeric();
281  CFX_LCNumeric(int64_t integral,
282                FX_DWORD fractional = 0,
283                int32_t exponent = 0);
284  CFX_LCNumeric(FX_FLOAT dbRetValue);
285  CFX_LCNumeric(double dbvalue);
286  CFX_LCNumeric(CFX_WideString& wsNumeric);
287
288  FX_FLOAT GetFloat() const;
289  double GetDouble() const;
290  CFX_WideString ToString() const;
291  CFX_WideString ToString(int32_t nTreading, FX_BOOL bTrimTailZeros) const;
292  int64_t m_Integral;
293  FX_DWORD m_Fractional;
294#ifdef FX_NUM_DOUBLE
295  CFX_WideString m_wsValue;
296#endif
297  int32_t m_Exponent;
298};
299static FX_BOOL FX_WStringToNumeric(const CFX_WideString& wsValue,
300                                   CFX_LCNumeric& lcnum) {
301  int64_t* pIntegral = &lcnum.m_Integral;
302  FX_DWORD* pFractional = &lcnum.m_Fractional;
303  int32_t* pExponent = &lcnum.m_Exponent;
304  *pIntegral = 0;
305  *pFractional = 0;
306  *pExponent = 0;
307#ifdef FX_NUM_DOUBLE
308  lcnum.m_wsValue.Empty();
309#endif
310  if (wsValue.IsEmpty()) {
311    return FALSE;
312  }
313  const int32_t nIntegralMaxLen = 17;
314  int32_t cc = 0;
315  FX_BOOL bNegative = FALSE, bExpSign = FALSE;
316  const FX_WCHAR* str = (const FX_WCHAR*)wsValue;
317  int32_t len = wsValue.GetLength();
318  while (cc < len && FX_IsSpace(str[cc])) {
319    cc++;
320  }
321  if (cc >= len) {
322    return FALSE;
323  }
324  if (str[cc] == '+') {
325    cc++;
326  } else if (str[cc] == '-') {
327    bNegative = TRUE;
328    cc++;
329  }
330  int32_t nIntegralLen = 0;
331  while (cc < len) {
332    if (str[cc] == '.') {
333      break;
334    }
335    if (!FX_IsDigit(str[cc])) {
336      if ((str[cc] == 'E' || str[cc] == 'e')) {
337        break;
338      } else {
339        return FALSE;
340      }
341    }
342    if (nIntegralLen < nIntegralMaxLen) {
343      *pIntegral = *pIntegral * 10 + str[cc] - '0';
344      nIntegralLen++;
345    }
346    cc++;
347  }
348  *pIntegral = bNegative ? -*pIntegral : *pIntegral;
349  if (cc < len && str[cc] == '.') {
350    int scale = 0;
351    double fraction = 0.0;
352    cc++;
353    while (cc < len) {
354      if (scale >= gs_fraction_count) {
355        while (cc < len) {
356          if (!FX_IsDigit(str[cc])) {
357            break;
358          }
359          cc++;
360        }
361      }
362      if (!FX_IsDigit(str[cc])) {
363        if ((str[cc] == 'E' || str[cc] == 'e')) {
364          break;
365        } else {
366          return FALSE;
367        }
368      }
369      fraction += gs_fraction_scales[scale] * (str[cc] - '0');
370      scale++;
371      cc++;
372    }
373    *pFractional = (FX_DWORD)(fraction * 4294967296.0);
374  }
375  if (cc < len && (str[cc] == 'E' || str[cc] == 'e')) {
376    cc++;
377    if (cc < len) {
378      if (str[cc] == '+') {
379        cc++;
380      } else if (str[cc] == '-') {
381        bExpSign = TRUE;
382        cc++;
383      }
384    }
385    while (cc < len) {
386      if (FX_IsDigit(str[cc])) {
387        return FALSE;
388      }
389      *pExponent = *pExponent * 10 + str[cc] - '0';
390      cc++;
391    }
392    *pExponent = bExpSign ? -*pExponent : *pExponent;
393  }
394#ifdef FX_NUM_DOUBLE
395  else {
396    lcnum.m_wsValue = wsValue;
397  }
398#endif
399  return TRUE;
400}
401CFX_LCNumeric::CFX_LCNumeric() {
402  m_Integral = 0;
403  m_Fractional = 0;
404  m_Exponent = 0;
405}
406CFX_LCNumeric::CFX_LCNumeric(int64_t integral,
407                             FX_DWORD fractional,
408                             int32_t exponent) {
409  m_Integral = integral;
410  m_Fractional = fractional;
411  m_Exponent = exponent;
412}
413CFX_LCNumeric::CFX_LCNumeric(FX_FLOAT dbRetValue) {
414  m_Integral = (int64_t)dbRetValue;
415  m_Fractional = (FX_DWORD)(((dbRetValue > 0) ? (dbRetValue - m_Integral)
416                                              : (m_Integral - dbRetValue)) *
417                            4294967296);
418  m_Exponent = 0;
419}
420CFX_LCNumeric::CFX_LCNumeric(double dbvalue) {
421  m_Integral = (int64_t)dbvalue;
422  m_Fractional = (FX_DWORD)(
423      ((dbvalue > 0) ? (dbvalue - m_Integral) : (m_Integral - dbvalue)) *
424      4294967296);
425  m_Exponent = 0;
426}
427CFX_LCNumeric::CFX_LCNumeric(CFX_WideString& wsNumeric) {
428  FX_WStringToNumeric(wsNumeric, *this);
429}
430FX_FLOAT CFX_LCNumeric::GetFloat() const {
431  FX_FLOAT dbRetValue = m_Fractional / 4294967296.0f;
432  dbRetValue = m_Integral + (m_Integral >= 0 ? dbRetValue : -dbRetValue);
433  if (m_Exponent != 0) {
434    dbRetValue *= FXSYS_pow(10, (FX_FLOAT)m_Exponent);
435  }
436  return dbRetValue;
437}
438double CFX_LCNumeric::GetDouble() const {
439  double value = m_Fractional / 4294967296.0;
440  value = m_Integral + (m_Integral >= 0 ? value : -value);
441  if (m_Exponent != 0) {
442    value *= FXSYS_pow(10, (FX_FLOAT)m_Exponent);
443  }
444  return value;
445}
446CFX_WideString CFX_LCNumeric::ToString() const {
447  return ToString(8, TRUE);
448}
449CFX_WideString CFX_LCNumeric::ToString(int32_t nTreading,
450                                       FX_BOOL bTrimTailZeros) const {
451#ifdef FX_NUM_DOUBLE
452  CFX_WideString wsResult;
453  if (!m_wsValue.IsEmpty()) {
454    const int32_t nIntegralMaxLen = 17;
455    int32_t cc = 0;
456    FX_BOOL bNegative = FALSE, bExpSign = FALSE;
457    const FX_WCHAR* str = (const FX_WCHAR*)m_wsValue;
458    int32_t len = m_wsValue.GetLength();
459    while (cc < len && FX_IsSpace(str[cc])) {
460      cc++;
461    }
462    if (cc >= len) {
463      return wsResult;
464    }
465    if (str[cc] == '+') {
466      cc++;
467    } else if (str[cc] == '-') {
468      bNegative = TRUE;
469      cc++;
470    }
471    int32_t nIntegralLen = 0;
472    while (cc < len) {
473      if (str[cc] == '.') {
474        break;
475      }
476      if (!FX_IsDigit(str[cc])) {
477        if ((str[cc] == 'E' || str[cc] == 'e')) {
478          break;
479        } else {
480          return wsResult;
481        }
482      }
483      if (nIntegralLen < nIntegralMaxLen) {
484        *pIntegral = *pIntegral * 10 + str[cc] - '0';
485        nIntegralLen++;
486      }
487      cc++;
488    }
489    *pIntegral = bNegative ? -*pIntegral : *pIntegral;
490    if (cc < len && str[cc] == '.') {
491      int scale = 0;
492      double fraction = 0.0;
493      cc++;
494      while (cc < len) {
495        if (scale >= gs_fraction_count) {
496          while (cc < len) {
497            if (!FX_IsDigit(str[cc])) {
498              break;
499            }
500            cc++;
501          }
502        }
503        if (!FX_IsDigit(str[cc])) {
504          if ((str[cc] == 'E' || str[cc] == 'e')) {
505            break;
506          } else {
507            return FALSE;
508          }
509        }
510        fraction += gs_fraction_scales[scale] * (str[cc] - '0');
511        scale++;
512        cc++;
513      }
514      *pFractional = (FX_DWORD)(fraction * 4294967296.0);
515    }
516  }
517  double dbValeu = GetDouble();
518  int64_t iInte = (int64_t)dbValeu;
519  wsResult.Format(L"%l", (int64_t)iInte);
520  if (m_Fractional) {
521    CFX_WideString wsFormat;
522    wsFormat.Format(L"%%.%dG", nTreading);
523    double dblMantissa = (dbValeu > 0) ? (dbValeu - iInte) : (iInte - dbValeu);
524    CFX_WideString wsFrac;
525    wsFrac.Format((const FX_WCHAR*)wsFormat, dblMantissa);
526    wsResult +=
527        CFX_WideStringC((const FX_WCHAR*)wsFrac + 1, wsFrac.GetLength() - 1);
528    if (bTrimTailZeros && nTreading > 0) {
529      wsResult.TrimRight(L"0");
530      wsResult.TrimRight(L".");
531    }
532  }
533#endif
534  CFX_WideString wsFormat;
535  wsFormat.Format(L"%%.%df", nTreading);
536  CFX_WideString wsResult;
537  wsResult.Format(wsFormat.c_str(), GetDouble());
538  if (bTrimTailZeros && nTreading > 0) {
539    wsResult.TrimRight(L"0");
540    wsResult.TrimRight(L".");
541  }
542  return wsResult;
543}
544IFX_FormatString* IFX_FormatString::Create(IFX_LocaleMgr* pLocaleMgr,
545                                           FX_BOOL bUseLCID) {
546  if (!pLocaleMgr) {
547    return NULL;
548  }
549  return new CFX_FormatString(pLocaleMgr, bUseLCID);
550}
551CFX_FormatString::CFX_FormatString(IFX_LocaleMgr* pLocaleMgr, FX_BOOL bUseLCID)
552    : m_pLocaleMgr(pLocaleMgr), m_bUseLCID(bUseLCID) {}
553CFX_FormatString::~CFX_FormatString() {}
554void CFX_FormatString::SplitFormatString(const CFX_WideString& wsFormatString,
555                                         CFX_WideStringArray& wsPatterns) {
556  int32_t iStrLen = wsFormatString.GetLength();
557  const FX_WCHAR* pStr = (const FX_WCHAR*)wsFormatString;
558  const FX_WCHAR* pToken = pStr;
559  const FX_WCHAR* pEnd = pStr + iStrLen;
560  FX_BOOL iQuote = FALSE;
561  while (TRUE) {
562    if (pStr >= pEnd) {
563      CFX_WideString sub(pToken, pStr - pToken);
564      wsPatterns.Add(sub);
565      return;
566    } else if (*pStr == '\'') {
567      iQuote = !iQuote;
568    } else if (*pStr == L'|' && !iQuote) {
569      CFX_WideString sub(pToken, pStr - pToken);
570      wsPatterns.Add(sub);
571      pToken = pStr + 1;
572    }
573    pStr++;
574  }
575}
576static CFX_WideString FX_GetLiteralText(const FX_WCHAR* pStrPattern,
577                                        int32_t& iPattern,
578                                        int32_t iLenPattern) {
579  CFX_WideString wsOutput;
580  if (pStrPattern[iPattern] != '\'') {
581    return wsOutput;
582  }
583  iPattern++;
584  int32_t iQuote = 1;
585  while (iPattern < iLenPattern) {
586    if (pStrPattern[iPattern] == '\'') {
587      iQuote++;
588      if ((iPattern + 1 >= iLenPattern) ||
589          ((pStrPattern[iPattern + 1] != '\'') && (iQuote % 2 == 0))) {
590        break;
591      } else {
592        iQuote++;
593      }
594      iPattern++;
595    } else if (pStrPattern[iPattern] == '\\' && (iPattern + 1 < iLenPattern) &&
596               pStrPattern[iPattern + 1] == 'u') {
597      int32_t iKeyValue = 0;
598      iPattern += 2;
599      int32_t i = 0;
600      while (iPattern < iLenPattern && i++ < 4) {
601        FX_WCHAR ch = pStrPattern[iPattern++];
602        if ((ch >= '0' && ch <= '9')) {
603          iKeyValue = iKeyValue * 16 + ch - '0';
604        } else if ((ch >= 'a' && ch <= 'f')) {
605          iKeyValue = iKeyValue * 16 + ch - 'a' + 10;
606        } else if ((ch >= 'A' && ch <= 'F')) {
607          iKeyValue = iKeyValue * 16 + ch - 'A' + 10;
608        }
609      }
610      if (iKeyValue != 0) {
611        wsOutput += (FX_WCHAR)(iKeyValue & 0x0000FFFF);
612      }
613      continue;
614    }
615    wsOutput += pStrPattern[iPattern++];
616  }
617  return wsOutput;
618}
619static CFX_WideString FX_GetLiteralTextReverse(const FX_WCHAR* pStrPattern,
620                                               int32_t& iPattern) {
621  CFX_WideString wsOutput;
622  if (pStrPattern[iPattern] != '\'') {
623    return wsOutput;
624  }
625  iPattern--;
626  int32_t iQuote = 1;
627  while (iPattern >= 0) {
628    if (pStrPattern[iPattern] == '\'') {
629      iQuote++;
630      if (iPattern - 1 >= 0 ||
631          ((pStrPattern[iPattern - 1] != '\'') && (iQuote % 2 == 0))) {
632        break;
633      } else {
634        iQuote++;
635      }
636      iPattern--;
637    } else if (pStrPattern[iPattern] == '\\' &&
638               pStrPattern[iPattern + 1] == 'u') {
639      iPattern--;
640      int32_t iKeyValue = 0;
641      int32_t iLen = wsOutput.GetLength();
642      int32_t i = 1;
643      for (; i < iLen && i < 5; i++) {
644        FX_WCHAR ch = wsOutput[i];
645        if ((ch >= '0' && ch <= '9')) {
646          iKeyValue = iKeyValue * 16 + ch - '0';
647        } else if ((ch >= 'a' && ch <= 'f')) {
648          iKeyValue = iKeyValue * 16 + ch - 'a' + 10;
649        } else if ((ch >= 'A' && ch <= 'F')) {
650          iKeyValue = iKeyValue * 16 + ch - 'A' + 10;
651        }
652      }
653      if (iKeyValue != 0) {
654        wsOutput.Delete(0, i);
655        wsOutput = (FX_WCHAR)(iKeyValue & 0x0000FFFF) + wsOutput;
656      }
657      continue;
658    }
659    wsOutput = pStrPattern[iPattern--] + wsOutput;
660  }
661  return wsOutput;
662}
663FX_LOCALECATEGORY CFX_FormatString::GetCategory(
664    const CFX_WideString& wsPattern) {
665  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
666  int32_t ccf = 0;
667  int32_t iLenf = wsPattern.GetLength();
668  const FX_WCHAR* pStr = (const FX_WCHAR*)wsPattern;
669  FX_BOOL bBraceOpen = FALSE;
670  while (ccf < iLenf) {
671    if (pStr[ccf] == '\'') {
672      FX_GetLiteralText(pStr, ccf, iLenf);
673    } else if (!bBraceOpen && FX_Local_Find(gs_wsConstChars, pStr[ccf]) < 0) {
674      CFX_WideString wsCategory(pStr[ccf]);
675      ccf++;
676      while (TRUE) {
677        if (ccf == iLenf) {
678          return eCategory;
679        }
680        if (pStr[ccf] == '.' || pStr[ccf] == '(') {
681          break;
682        }
683        if (pStr[ccf] == '{') {
684          bBraceOpen = TRUE;
685          break;
686        }
687        wsCategory += pStr[ccf];
688        ccf++;
689      }
690      FX_DWORD dwHash =
691          FX_HashCode_String_GetW(wsCategory, wsCategory.GetLength());
692      if (dwHash == FX_LOCALECATEGORY_DateHash) {
693        if (eCategory == FX_LOCALECATEGORY_Time) {
694          return FX_LOCALECATEGORY_DateTime;
695        }
696        eCategory = FX_LOCALECATEGORY_Date;
697      } else if (dwHash == FX_LOCALECATEGORY_TimeHash) {
698        if (eCategory == FX_LOCALECATEGORY_Date) {
699          return FX_LOCALECATEGORY_DateTime;
700        }
701        eCategory = FX_LOCALECATEGORY_Time;
702      } else if (dwHash == FX_LOCALECATEGORY_DateTimeHash) {
703        return FX_LOCALECATEGORY_DateTime;
704      } else if (dwHash == FX_LOCALECATEGORY_TextHash) {
705        return FX_LOCALECATEGORY_Text;
706      } else if (dwHash == FX_LOCALECATEGORY_NumHash) {
707        return FX_LOCALECATEGORY_Num;
708      } else if (dwHash == FX_LOCALECATEGORY_ZeroHash) {
709        return FX_LOCALECATEGORY_Zero;
710      } else if (dwHash == FX_LOCALECATEGORY_NullHash) {
711        return FX_LOCALECATEGORY_Null;
712      }
713    } else if (pStr[ccf] == '}') {
714      bBraceOpen = FALSE;
715    }
716    ccf++;
717  }
718  return eCategory;
719}
720static FX_WORD FX_WStringToLCID(const FX_WCHAR* pstrLCID) {
721  if (!pstrLCID) {
722    return 0;
723  }
724  wchar_t* pEnd;
725  return (FX_WORD)wcstol((wchar_t*)pstrLCID, &pEnd, 16);
726}
727FX_WORD CFX_FormatString::GetLCID(const CFX_WideString& wsPattern) {
728  return FX_WStringToLCID(GetLocaleName(wsPattern));
729}
730CFX_WideString CFX_FormatString::GetLocaleName(
731    const CFX_WideString& wsPattern) {
732  int32_t ccf = 0;
733  int32_t iLenf = wsPattern.GetLength();
734  const FX_WCHAR* pStr = (const FX_WCHAR*)wsPattern;
735  while (ccf < iLenf) {
736    if (pStr[ccf] == '\'') {
737      FX_GetLiteralText(pStr, ccf, iLenf);
738    } else if (pStr[ccf] == '(') {
739      ccf++;
740      CFX_WideString wsLCID;
741      while (ccf < iLenf && pStr[ccf] != ')') {
742        wsLCID += pStr[ccf++];
743      }
744      return wsLCID;
745    }
746    ccf++;
747  }
748  return CFX_WideString();
749}
750IFX_Locale* CFX_FormatString::GetTextFormat(const CFX_WideString& wsPattern,
751                                            const CFX_WideStringC& wsCategory,
752                                            CFX_WideString& wsPurgePattern) {
753  IFX_Locale* pLocale = NULL;
754  int32_t ccf = 0;
755  int32_t iLenf = wsPattern.GetLength();
756  const FX_WCHAR* pStr = (const FX_WCHAR*)wsPattern;
757  FX_BOOL bBrackOpen = FALSE;
758  while (ccf < iLenf) {
759    if (pStr[ccf] == '\'') {
760      int32_t iCurChar = ccf;
761      FX_GetLiteralText(pStr, ccf, iLenf);
762      wsPurgePattern += CFX_WideStringC(pStr + iCurChar, ccf - iCurChar + 1);
763    } else if (!bBrackOpen && FX_Local_Find(gs_wsConstChars, pStr[ccf]) < 0) {
764      CFX_WideString wsSearchCategory(pStr[ccf]);
765      ccf++;
766      while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
767             pStr[ccf] != '(') {
768        wsSearchCategory += pStr[ccf];
769        ccf++;
770      }
771      if (wsSearchCategory != wsCategory) {
772        continue;
773      }
774      while (ccf < iLenf) {
775        if (pStr[ccf] == '(') {
776          ccf++;
777          CFX_WideString wsLCID;
778          while (ccf < iLenf && pStr[ccf] != ')') {
779            wsLCID += pStr[ccf++];
780          }
781          pLocale = GetPatternLocale(wsLCID);
782        } else if (pStr[ccf] == '{') {
783          bBrackOpen = TRUE;
784          break;
785        }
786        ccf++;
787      }
788    } else if (pStr[ccf] != '}') {
789      wsPurgePattern += pStr[ccf];
790    }
791    ccf++;
792  }
793  if (!bBrackOpen) {
794    wsPurgePattern = wsPattern;
795  }
796  if (!pLocale) {
797    pLocale = m_pLocaleMgr->GetDefLocale();
798  }
799  return pLocale;
800}
801#define FX_NUMSTYLE_Percent 0x01
802#define FX_NUMSTYLE_Exponent 0x02
803#define FX_NUMSTYLE_DotVorv 0x04
804IFX_Locale* CFX_FormatString::GetNumericFormat(const CFX_WideString& wsPattern,
805                                               int32_t& iDotIndex,
806                                               FX_DWORD& dwStyle,
807                                               CFX_WideString& wsPurgePattern) {
808  dwStyle = 0;
809  IFX_Locale* pLocale = NULL;
810  int32_t ccf = 0;
811  int32_t iLenf = wsPattern.GetLength();
812  const FX_WCHAR* pStr = (const FX_WCHAR*)wsPattern;
813  FX_BOOL bFindDot = FALSE;
814  FX_BOOL bBrackOpen = FALSE;
815  while (ccf < iLenf) {
816    if (pStr[ccf] == '\'') {
817      int32_t iCurChar = ccf;
818      FX_GetLiteralText(pStr, ccf, iLenf);
819      wsPurgePattern += CFX_WideStringC(pStr + iCurChar, ccf - iCurChar + 1);
820    } else if (!bBrackOpen && FX_Local_Find(gs_wsConstChars, pStr[ccf]) < 0) {
821      CFX_WideString wsCategory(pStr[ccf]);
822      ccf++;
823      while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
824             pStr[ccf] != '(') {
825        wsCategory += pStr[ccf];
826        ccf++;
827      }
828      if (wsCategory != FX_WSTRC(L"num")) {
829        bBrackOpen = TRUE;
830        ccf = 0;
831        continue;
832      }
833      while (ccf < iLenf) {
834        if (pStr[ccf] == '(') {
835          ccf++;
836          CFX_WideString wsLCID;
837          while (ccf < iLenf && pStr[ccf] != ')') {
838            wsLCID += pStr[ccf++];
839          }
840          pLocale = GetPatternLocale(wsLCID);
841        } else if (pStr[ccf] == '{') {
842          bBrackOpen = TRUE;
843          break;
844        } else if (pStr[ccf] == '.') {
845          CFX_WideString wsSubCategory;
846          ccf++;
847          while (ccf < iLenf && pStr[ccf] != '(' && pStr[ccf] != '{') {
848            wsSubCategory += pStr[ccf++];
849          }
850          FX_DWORD dwSubHash =
851              FX_HashCode_String_GetW(wsSubCategory, wsSubCategory.GetLength());
852          FX_LOCALENUMSUBCATEGORY eSubCategory = FX_LOCALENUMPATTERN_Decimal;
853          for (int32_t i = 0; i < g_iFXLocaleNumSubCatCount; i++) {
854            if (g_FXLocaleNumSubCatData[i].uHash == dwSubHash) {
855              eSubCategory = (FX_LOCALENUMSUBCATEGORY)g_FXLocaleNumSubCatData[i]
856                                 .eSubCategory;
857              break;
858            }
859          }
860          wsSubCategory.Empty();
861          if (!pLocale) {
862            pLocale = m_pLocaleMgr->GetDefLocale();
863          }
864          FXSYS_assert(pLocale != NULL);
865          pLocale->GetNumPattern(eSubCategory, wsSubCategory);
866          iDotIndex = wsSubCategory.Find('.');
867          if (iDotIndex > 0) {
868            iDotIndex += wsPurgePattern.GetLength();
869            bFindDot = TRUE;
870            dwStyle |= FX_NUMSTYLE_DotVorv;
871          }
872          wsPurgePattern += wsSubCategory;
873          if (eSubCategory == FX_LOCALENUMPATTERN_Percent) {
874            dwStyle |= FX_NUMSTYLE_Percent;
875          }
876          continue;
877        }
878        ccf++;
879      }
880    } else if (pStr[ccf] == 'E') {
881      dwStyle |= FX_NUMSTYLE_Exponent;
882      wsPurgePattern += pStr[ccf];
883    } else if (pStr[ccf] == '%') {
884      dwStyle |= FX_NUMSTYLE_Percent;
885      wsPurgePattern += pStr[ccf];
886    } else if (pStr[ccf] != '}') {
887      wsPurgePattern += pStr[ccf];
888    }
889    if (!bFindDot) {
890      if (pStr[ccf] == '.' || pStr[ccf] == 'V' || pStr[ccf] == 'v') {
891        bFindDot = TRUE;
892        iDotIndex = wsPurgePattern.GetLength() - 1;
893        dwStyle |= FX_NUMSTYLE_DotVorv;
894      }
895    }
896    ccf++;
897  }
898  if (!bFindDot) {
899    iDotIndex = wsPurgePattern.GetLength();
900  }
901  if (!pLocale) {
902    pLocale = m_pLocaleMgr->GetDefLocale();
903  }
904  return pLocale;
905}
906static FX_BOOL FX_GetNumericDotIndex(const CFX_WideString& wsNum,
907                                     const CFX_WideString& wsDotSymbol,
908                                     int32_t& iDotIndex) {
909  int32_t ccf = 0;
910  int32_t iLenf = wsNum.GetLength();
911  const FX_WCHAR* pStr = (const FX_WCHAR*)wsNum;
912  int32_t iLenDot = wsDotSymbol.GetLength();
913  while (ccf < iLenf) {
914    if (pStr[ccf] == '\'') {
915      FX_GetLiteralText(pStr, ccf, iLenf);
916    } else if (ccf + iLenDot <= iLenf &&
917               !FXSYS_wcsncmp(pStr + ccf, (const FX_WCHAR*)wsDotSymbol,
918                              iLenDot)) {
919      iDotIndex = ccf;
920      return TRUE;
921    }
922    ccf++;
923  }
924  iDotIndex = wsNum.Find('.');
925  if (iDotIndex < 0) {
926    iDotIndex = iLenf;
927    return FALSE;
928  }
929  return TRUE;
930}
931FX_BOOL CFX_FormatString::ParseText(const CFX_WideString& wsSrcText,
932                                    const CFX_WideString& wsPattern,
933                                    CFX_WideString& wsValue) {
934  wsValue.Empty();
935  if (wsSrcText.IsEmpty() || wsPattern.IsEmpty()) {
936    return FALSE;
937  }
938  CFX_WideString wsTextFormat;
939  GetTextFormat(wsPattern, FX_WSTRC(L"text"), wsTextFormat);
940  if (wsTextFormat.IsEmpty()) {
941    return FALSE;
942  }
943  int32_t iText = 0, iPattern = 0;
944  const FX_WCHAR* pStrText = (const FX_WCHAR*)wsSrcText;
945  int32_t iLenText = wsSrcText.GetLength();
946  const FX_WCHAR* pStrPattern = (const FX_WCHAR*)wsTextFormat;
947  int32_t iLenPattern = wsTextFormat.GetLength();
948  while (iPattern < iLenPattern && iText < iLenText) {
949    switch (pStrPattern[iPattern]) {
950      case '\'': {
951        CFX_WideString wsLiteral =
952            FX_GetLiteralText(pStrPattern, iPattern, iLenPattern);
953        int32_t iLiteralLen = wsLiteral.GetLength();
954        if (iText + iLiteralLen > iLenText ||
955            FXSYS_wcsncmp(pStrText + iText, (const FX_WCHAR*)wsLiteral,
956                          iLiteralLen)) {
957          wsValue = wsSrcText;
958          return FALSE;
959        }
960        iText += iLiteralLen;
961        iPattern++;
962        break;
963      }
964      case 'A':
965        if (FX_IsAlpha(pStrText[iText])) {
966          wsValue += pStrText[iText];
967          iText++;
968        }
969        iPattern++;
970        break;
971      case 'X':
972        wsValue += pStrText[iText];
973        iText++;
974        iPattern++;
975        break;
976      case 'O':
977      case '0':
978        if (FX_IsDigit(pStrText[iText]) || FX_IsAlpha(pStrText[iText])) {
979          wsValue += pStrText[iText];
980          iText++;
981        }
982        iPattern++;
983        break;
984      case '9':
985        if (FX_IsDigit(pStrText[iText])) {
986          wsValue += pStrText[iText];
987          iText++;
988        }
989        iPattern++;
990        break;
991      default:
992        if (pStrPattern[iPattern] != pStrText[iText]) {
993          wsValue = wsSrcText;
994          return FALSE;
995        }
996        iPattern++;
997        iText++;
998        break;
999    }
1000  }
1001  return iPattern == iLenPattern && iText == iLenText;
1002}
1003FX_BOOL CFX_FormatString::ParseNum(const CFX_WideString& wsSrcNum,
1004                                   const CFX_WideString& wsPattern,
1005                                   FX_FLOAT& fValue) {
1006  fValue = 0.0f;
1007  if (wsSrcNum.IsEmpty() || wsPattern.IsEmpty()) {
1008    return FALSE;
1009  }
1010  int32_t dot_index_f = -1;
1011  FX_DWORD dwFormatStyle = 0;
1012  CFX_WideString wsNumFormat;
1013  IFX_Locale* pLocale =
1014      GetNumericFormat(wsPattern, dot_index_f, dwFormatStyle, wsNumFormat);
1015  if (!pLocale || wsNumFormat.IsEmpty()) {
1016    return FALSE;
1017  }
1018  int32_t iExponent = 0;
1019  CFX_WideString wsDotSymbol;
1020  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal, wsDotSymbol);
1021  CFX_WideString wsGroupSymbol;
1022  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping, wsGroupSymbol);
1023  int32_t iGroupLen = wsGroupSymbol.GetLength();
1024  CFX_WideString wsMinus;
1025  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinus);
1026  int32_t iMinusLen = wsMinus.GetLength();
1027  int cc = 0, ccf = 0;
1028  const FX_WCHAR* str = (const FX_WCHAR*)wsSrcNum;
1029  int len = wsSrcNum.GetLength();
1030  const FX_WCHAR* strf = (const FX_WCHAR*)wsNumFormat;
1031  int lenf = wsNumFormat.GetLength();
1032  double dbRetValue = 0;
1033  double coeff = 1;
1034  FX_BOOL bHavePercentSymbol = FALSE;
1035  FX_BOOL bNeg = FALSE;
1036  FX_BOOL bReverseParse = FALSE;
1037  int32_t dot_index = 0;
1038  if (!FX_GetNumericDotIndex(wsSrcNum, wsDotSymbol, dot_index) &&
1039      (dwFormatStyle & FX_NUMSTYLE_DotVorv)) {
1040    bReverseParse = TRUE;
1041  }
1042  bReverseParse = FALSE;
1043  if (bReverseParse) {
1044    ccf = lenf - 1;
1045    cc = len - 1;
1046    while (ccf > dot_index_f && cc >= 0) {
1047      switch (strf[ccf]) {
1048        case '\'': {
1049          CFX_WideString wsLiteral = FX_GetLiteralTextReverse(strf, ccf);
1050          int32_t iLiteralLen = wsLiteral.GetLength();
1051          cc -= iLiteralLen - 1;
1052          if (cc < 0 || FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsLiteral,
1053                                      iLiteralLen)) {
1054            return FALSE;
1055          }
1056          cc--;
1057          ccf--;
1058          break;
1059        }
1060        case '9':
1061          if (!FX_IsDigit(str[cc])) {
1062            return FALSE;
1063          }
1064          dbRetValue = dbRetValue * coeff + (str[cc] - '0') * 0.1;
1065          coeff *= 0.1;
1066          cc--;
1067          ccf--;
1068          break;
1069        case 'z':
1070          if (cc >= 0) {
1071            dbRetValue = dbRetValue * coeff + (str[cc] - '0') * 0.1;
1072            coeff *= 0.1;
1073            cc--;
1074          }
1075          ccf--;
1076          break;
1077        case 'Z':
1078          if (str[cc] != ' ') {
1079            dbRetValue = dbRetValue * coeff + (str[cc] - '0') * 0.1;
1080            coeff *= 0.1;
1081          }
1082          cc--;
1083          ccf--;
1084          break;
1085        case 'S':
1086          if (str[cc] == '+' || str[cc] == ' ') {
1087            cc--;
1088          } else {
1089            cc -= iMinusLen - 1;
1090            if (cc < 0 ||
1091                FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
1092              return FALSE;
1093            }
1094            cc--;
1095            bNeg = TRUE;
1096          }
1097          ccf--;
1098          break;
1099        case 's':
1100          if (str[cc] == '+') {
1101            cc--;
1102          } else {
1103            cc -= iMinusLen - 1;
1104            if (cc < 0 ||
1105                FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
1106              return FALSE;
1107            }
1108            cc--;
1109            bNeg = TRUE;
1110          }
1111          ccf--;
1112          break;
1113        case 'E': {
1114          if (cc >= dot_index) {
1115            return FALSE;
1116          }
1117          FX_BOOL bExpSign = FALSE;
1118          while (cc >= 0) {
1119            if (str[cc] == 'E' || str[cc] == 'e') {
1120              break;
1121            }
1122            if (FX_IsDigit(str[cc])) {
1123              iExponent = iExponent + (str[cc] - '0') * 10;
1124              cc--;
1125              continue;
1126            } else if (str[cc] == '+') {
1127              cc--;
1128              continue;
1129            } else if (cc - iMinusLen + 1 > 0 &&
1130                       !FXSYS_wcsncmp(str + (cc - iMinusLen + 1),
1131                                      (const FX_WCHAR*)wsMinus, iMinusLen)) {
1132              bExpSign = TRUE;
1133              cc -= iMinusLen;
1134            } else {
1135              return FALSE;
1136            }
1137          }
1138          cc--;
1139          iExponent = bExpSign ? -iExponent : iExponent;
1140          ccf--;
1141        } break;
1142        case '$': {
1143          CFX_WideString wsSymbol;
1144          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol,
1145                                     wsSymbol);
1146          int32_t iSymbolLen = wsSymbol.GetLength();
1147          cc -= iSymbolLen - 1;
1148          if (cc < 0 ||
1149              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol, iSymbolLen)) {
1150            return FALSE;
1151          }
1152          cc--;
1153          ccf--;
1154        } break;
1155        case 'r':
1156          if (ccf - 1 >= 0 && strf[ccf - 1] == 'c') {
1157            if (str[cc] == 'R' && cc - 1 >= 0 && str[cc - 1] == 'C') {
1158              bNeg = TRUE;
1159              cc -= 2;
1160            }
1161            ccf -= 2;
1162          } else {
1163            ccf--;
1164          }
1165          break;
1166        case 'R':
1167          if (ccf - 1 >= 0 && strf[ccf - 1] == 'C') {
1168            if (str[cc] == ' ') {
1169              cc++;
1170            } else if (str[cc] == 'R' && cc - 1 >= 0 && str[cc - 1] == 'C') {
1171              bNeg = TRUE;
1172              cc -= 2;
1173            }
1174            ccf -= 2;
1175          } else {
1176            ccf--;
1177          }
1178          break;
1179        case 'b':
1180          if (ccf - 1 >= 0 && strf[ccf - 1] == 'd') {
1181            if (str[cc] == 'B' && cc - 1 >= 0 && str[cc - 1] == 'D') {
1182              bNeg = TRUE;
1183              cc -= 2;
1184            }
1185            ccf -= 2;
1186          } else {
1187            ccf--;
1188          }
1189          break;
1190        case 'B':
1191          if (ccf - 1 >= 0 && strf[ccf - 1] == 'D') {
1192            if (str[cc] == ' ') {
1193              cc++;
1194            } else if (str[cc] == 'B' && cc - 1 >= 0 && str[cc - 1] == 'D') {
1195              bNeg = TRUE;
1196              cc -= 2;
1197            }
1198            ccf -= 2;
1199          } else {
1200            ccf--;
1201          }
1202          break;
1203        case '.':
1204        case 'V':
1205        case 'v':
1206          return FALSE;
1207        case '%': {
1208          CFX_WideString wsSymbol;
1209          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent, wsSymbol);
1210          int32_t iSysmbolLen = wsSymbol.GetLength();
1211          cc -= iSysmbolLen - 1;
1212          if (cc < 0 ||
1213              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol, iSysmbolLen)) {
1214            return FALSE;
1215          }
1216          cc--;
1217          ccf--;
1218          bHavePercentSymbol = TRUE;
1219        } break;
1220        case '8':
1221          while (ccf < lenf && strf[ccf] == '8') {
1222            ccf++;
1223          }
1224          while (cc < len && FX_IsDigit(str[cc])) {
1225            dbRetValue = (str[cc] - '0') * coeff + dbRetValue;
1226            coeff *= 0.1;
1227            cc++;
1228          }
1229          break;
1230        case ',': {
1231          if (cc >= 0) {
1232            cc -= iGroupLen - 1;
1233            if (cc >= 0 &&
1234                FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsGroupSymbol,
1235                              iGroupLen) == 0) {
1236              cc--;
1237            } else {
1238              cc += iGroupLen - 1;
1239            }
1240          }
1241          ccf--;
1242        } break;
1243        case '(':
1244          if (str[cc] == L'(') {
1245            bNeg = TRUE;
1246          } else if (str[cc] != L' ') {
1247            return FALSE;
1248          }
1249          cc--;
1250          ccf--;
1251          break;
1252        case ')':
1253          if (str[cc] == L')') {
1254            bNeg = TRUE;
1255          } else if (str[cc] != L' ') {
1256            return FALSE;
1257          }
1258          cc--;
1259          ccf--;
1260          break;
1261        default:
1262          if (strf[ccf] != str[cc]) {
1263            return FALSE;
1264          }
1265          cc--;
1266          ccf--;
1267      }
1268    }
1269    dot_index = cc + 1;
1270  }
1271  ccf = dot_index_f - 1;
1272  cc = dot_index - 1;
1273  coeff = 1;
1274  while (ccf >= 0 && cc >= 0) {
1275    switch (strf[ccf]) {
1276      case '\'': {
1277        CFX_WideString wsLiteral = FX_GetLiteralTextReverse(strf, ccf);
1278        int32_t iLiteralLen = wsLiteral.GetLength();
1279        cc -= iLiteralLen - 1;
1280        if (cc < 0 ||
1281            FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsLiteral, iLiteralLen)) {
1282          return FALSE;
1283        }
1284        cc--;
1285        ccf--;
1286        break;
1287      }
1288      case '9':
1289        if (!FX_IsDigit(str[cc])) {
1290          return FALSE;
1291        }
1292        dbRetValue = dbRetValue + (str[cc] - '0') * coeff;
1293        coeff *= 10;
1294        cc--;
1295        ccf--;
1296        break;
1297      case 'z':
1298        if (FX_IsDigit(str[cc])) {
1299          dbRetValue = dbRetValue + (str[cc] - '0') * coeff;
1300          coeff *= 10;
1301          cc--;
1302        }
1303        ccf--;
1304        break;
1305      case 'Z':
1306        if (str[cc] != ' ') {
1307          if (FX_IsDigit(str[cc])) {
1308            dbRetValue = dbRetValue + (str[cc] - '0') * coeff;
1309            coeff *= 10;
1310            cc--;
1311          }
1312        } else {
1313          cc--;
1314        }
1315        ccf--;
1316        break;
1317      case 'S':
1318        if (str[cc] == '+' || str[cc] == ' ') {
1319          cc--;
1320        } else {
1321          cc -= iMinusLen - 1;
1322          if (cc < 0 ||
1323              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
1324            return FALSE;
1325          }
1326          cc--;
1327          bNeg = TRUE;
1328        }
1329        ccf--;
1330        break;
1331      case 's':
1332        if (str[cc] == '+') {
1333          cc--;
1334        } else {
1335          cc -= iMinusLen - 1;
1336          if (cc < 0 ||
1337              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
1338            return FALSE;
1339          }
1340          cc--;
1341          bNeg = TRUE;
1342        }
1343        ccf--;
1344        break;
1345      case 'E': {
1346        if (cc >= dot_index) {
1347          return FALSE;
1348        }
1349        FX_BOOL bExpSign = FALSE;
1350        while (cc >= 0) {
1351          if (str[cc] == 'E' || str[cc] == 'e') {
1352            break;
1353          }
1354          if (FX_IsDigit(str[cc])) {
1355            iExponent = iExponent + (str[cc] - '0') * 10;
1356            cc--;
1357            continue;
1358          } else if (str[cc] == '+') {
1359            cc--;
1360            continue;
1361          } else if (cc - iMinusLen + 1 > 0 &&
1362                     !FXSYS_wcsncmp(str + (cc - iMinusLen + 1),
1363                                    (const FX_WCHAR*)wsMinus, iMinusLen)) {
1364            bExpSign = TRUE;
1365            cc -= iMinusLen;
1366          } else {
1367            return FALSE;
1368          }
1369        }
1370        cc--;
1371        iExponent = bExpSign ? -iExponent : iExponent;
1372        ccf--;
1373      } break;
1374      case '$': {
1375        CFX_WideString wsSymbol;
1376        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol, wsSymbol);
1377        int32_t iSymbolLen = wsSymbol.GetLength();
1378        cc -= iSymbolLen - 1;
1379        if (cc < 0 ||
1380            FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol, iSymbolLen)) {
1381          return FALSE;
1382        }
1383        cc--;
1384        ccf--;
1385      } break;
1386      case 'r':
1387        if (ccf - 1 >= 0 && strf[ccf - 1] == 'c') {
1388          if (str[cc] == 'R' && cc - 1 >= 0 && str[cc - 1] == 'C') {
1389            bNeg = TRUE;
1390            cc -= 2;
1391          }
1392          ccf -= 2;
1393        } else {
1394          ccf--;
1395        }
1396        break;
1397      case 'R':
1398        if (ccf - 1 >= 0 && strf[ccf - 1] == 'C') {
1399          if (str[cc] == ' ') {
1400            cc++;
1401          } else if (str[cc] == 'R' && cc - 1 >= 0 && str[cc - 1] == 'C') {
1402            bNeg = TRUE;
1403            cc -= 2;
1404          }
1405          ccf -= 2;
1406        } else {
1407          ccf--;
1408        }
1409        break;
1410      case 'b':
1411        if (ccf - 1 >= 0 && strf[ccf - 1] == 'd') {
1412          if (str[cc] == 'B' && cc - 1 >= 0 && str[cc - 1] == 'D') {
1413            bNeg = TRUE;
1414            cc -= 2;
1415          }
1416          ccf -= 2;
1417        } else {
1418          ccf--;
1419        }
1420        break;
1421      case 'B':
1422        if (ccf - 1 >= 0 && strf[ccf - 1] == 'D') {
1423          if (str[cc] == ' ') {
1424            cc++;
1425          } else if (str[cc] == 'B' && cc - 1 >= 0 && str[cc - 1] == 'D') {
1426            bNeg = TRUE;
1427            cc -= 2;
1428          }
1429          ccf -= 2;
1430        } else {
1431          ccf--;
1432        }
1433        break;
1434      case '.':
1435      case 'V':
1436      case 'v':
1437        return FALSE;
1438      case '%': {
1439        CFX_WideString wsSymbol;
1440        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent, wsSymbol);
1441        int32_t iSysmbolLen = wsSymbol.GetLength();
1442        cc -= iSysmbolLen - 1;
1443        if (cc < 0 ||
1444            FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol, iSysmbolLen)) {
1445          return FALSE;
1446        }
1447        cc--;
1448        ccf--;
1449        bHavePercentSymbol = TRUE;
1450      } break;
1451      case '8':
1452        return FALSE;
1453      case ',': {
1454        if (cc >= 0) {
1455          cc -= iGroupLen - 1;
1456          if (cc >= 0 &&
1457              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsGroupSymbol,
1458                            iGroupLen) == 0) {
1459            cc--;
1460          } else {
1461            cc += iGroupLen - 1;
1462          }
1463        }
1464        ccf--;
1465      } break;
1466      case '(':
1467        if (str[cc] == L'(') {
1468          bNeg = TRUE;
1469        } else if (str[cc] != L' ') {
1470          return FALSE;
1471        }
1472        cc--;
1473        ccf--;
1474        break;
1475      case ')':
1476        if (str[cc] == L')') {
1477          bNeg = TRUE;
1478        } else if (str[cc] != L' ') {
1479          return FALSE;
1480        }
1481        cc--;
1482        ccf--;
1483        break;
1484      default:
1485        if (strf[ccf] != str[cc]) {
1486          return FALSE;
1487        }
1488        cc--;
1489        ccf--;
1490    }
1491  }
1492  if (cc >= 0) {
1493    return FALSE;
1494  }
1495  if (!bReverseParse) {
1496    ccf = dot_index_f + 1;
1497    cc = (dot_index == len) ? len : dot_index + 1;
1498    coeff = 0.1;
1499    while (cc < len && ccf < lenf) {
1500      switch (strf[ccf]) {
1501        case '\'': {
1502          CFX_WideString wsLiteral = FX_GetLiteralText(strf, ccf, lenf);
1503          int32_t iLiteralLen = wsLiteral.GetLength();
1504          if (cc + iLiteralLen > len ||
1505              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsLiteral,
1506                            iLiteralLen)) {
1507            return FALSE;
1508          }
1509          cc += iLiteralLen;
1510          ccf++;
1511          break;
1512        }
1513        case '9':
1514          if (!FX_IsDigit(str[cc])) {
1515            return FALSE;
1516          }
1517          {
1518            dbRetValue = dbRetValue + (str[cc] - '0') * coeff;
1519            coeff *= 0.1;
1520          }
1521          cc++;
1522          ccf++;
1523          break;
1524        case 'z':
1525          if (FX_IsDigit(str[cc])) {
1526            dbRetValue = dbRetValue + (str[cc] - '0') * coeff;
1527            coeff *= 0.1;
1528            cc++;
1529          }
1530          ccf++;
1531          break;
1532        case 'Z':
1533          if (str[cc] != ' ') {
1534            if (FX_IsDigit(str[cc])) {
1535              dbRetValue = dbRetValue + (str[cc] - '0') * coeff;
1536              coeff *= 0.1;
1537              cc++;
1538            }
1539          } else {
1540            cc++;
1541          }
1542          ccf++;
1543          break;
1544        case 'S':
1545          if (str[cc] == '+' || str[cc] == ' ') {
1546            cc++;
1547          } else {
1548            if (cc + iMinusLen > len ||
1549                FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
1550              return FALSE;
1551            }
1552            bNeg = TRUE;
1553            cc += iMinusLen;
1554          }
1555          ccf++;
1556          break;
1557        case 's':
1558          if (str[cc] == '+') {
1559            cc++;
1560          } else {
1561            if (cc + iMinusLen > len ||
1562                FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
1563              return FALSE;
1564            }
1565            bNeg = TRUE;
1566            cc += iMinusLen;
1567          }
1568          ccf++;
1569          break;
1570        case 'E': {
1571          if (cc >= len || (str[cc] != 'E' && str[cc] != 'e')) {
1572            return FALSE;
1573          }
1574          FX_BOOL bExpSign = FALSE;
1575          cc++;
1576          if (cc < len) {
1577            if (str[cc] == '+') {
1578              cc++;
1579            } else if (str[cc] == '-') {
1580              bExpSign = TRUE;
1581              cc++;
1582            }
1583          }
1584          while (cc < len) {
1585            if (!FX_IsDigit(str[cc])) {
1586              break;
1587            }
1588            iExponent = iExponent * 10 + str[cc] - '0';
1589            cc++;
1590          }
1591          iExponent = bExpSign ? -iExponent : iExponent;
1592          ccf++;
1593        } break;
1594        case '$': {
1595          CFX_WideString wsSymbol;
1596          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol,
1597                                     wsSymbol);
1598          int32_t iSymbolLen = wsSymbol.GetLength();
1599          if (cc + iSymbolLen > len ||
1600              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol, iSymbolLen)) {
1601            return FALSE;
1602          }
1603          cc += iSymbolLen;
1604          ccf++;
1605        } break;
1606        case 'c':
1607          if (ccf + 1 < lenf && strf[ccf + 1] == 'r') {
1608            if (str[cc] == 'C' && cc + 1 < len && str[cc + 1] == 'R') {
1609              bNeg = TRUE;
1610              cc += 2;
1611            }
1612            ccf += 2;
1613          }
1614          break;
1615        case 'C':
1616          if (ccf + 1 < lenf && strf[ccf + 1] == 'R') {
1617            if (str[cc] == ' ') {
1618              cc++;
1619            } else if (str[cc] == 'C' && cc + 1 < len && str[cc + 1] == 'R') {
1620              bNeg = TRUE;
1621              cc += 2;
1622            }
1623            ccf += 2;
1624          }
1625          break;
1626        case 'd':
1627          if (ccf + 1 < lenf && strf[ccf + 1] == 'b') {
1628            if (str[cc] == 'D' && cc + 1 < len && str[cc + 1] == 'B') {
1629              bNeg = TRUE;
1630              cc += 2;
1631            }
1632            ccf += 2;
1633          }
1634          break;
1635        case 'D':
1636          if (ccf + 1 < lenf && strf[ccf + 1] == 'B') {
1637            if (str[cc] == ' ') {
1638              cc++;
1639            } else if (str[cc] == 'D' && cc + 1 < len && str[cc + 1] == 'B') {
1640              bNeg = TRUE;
1641              cc += 2;
1642            }
1643            ccf += 2;
1644          }
1645          break;
1646        case '.':
1647        case 'V':
1648        case 'v':
1649          return FALSE;
1650        case '%': {
1651          CFX_WideString wsSymbol;
1652          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent, wsSymbol);
1653          int32_t iSysmbolLen = wsSymbol.GetLength();
1654          if (cc + iSysmbolLen <= len &&
1655              !FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol,
1656                             iSysmbolLen)) {
1657            cc += iSysmbolLen;
1658          }
1659          ccf++;
1660          bHavePercentSymbol = TRUE;
1661        } break;
1662        case '8': {
1663          while (ccf < lenf && strf[ccf] == '8') {
1664            ccf++;
1665          }
1666          while (cc < len && FX_IsDigit(str[cc])) {
1667            dbRetValue = (str[cc] - '0') * coeff + dbRetValue;
1668            coeff *= 0.1;
1669            cc++;
1670          }
1671        } break;
1672        case ',': {
1673          if (cc + iGroupLen <= len &&
1674              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsGroupSymbol,
1675                            iGroupLen) == 0) {
1676            cc += iGroupLen;
1677          }
1678          ccf++;
1679        } break;
1680        case '(':
1681          if (str[cc] == L'(') {
1682            bNeg = TRUE;
1683          } else if (str[cc] != L' ') {
1684            return FALSE;
1685          }
1686          cc++;
1687          ccf++;
1688          break;
1689        case ')':
1690          if (str[cc] == L')') {
1691            bNeg = TRUE;
1692          } else if (str[cc] != L' ') {
1693            return FALSE;
1694          }
1695          cc++;
1696          ccf++;
1697          break;
1698        default:
1699          if (strf[ccf] != str[cc]) {
1700            return FALSE;
1701          }
1702          cc++;
1703          ccf++;
1704      }
1705    }
1706    if (cc != len) {
1707      return FALSE;
1708    }
1709  }
1710  if (iExponent) {
1711    dbRetValue *= FXSYS_pow(10, (FX_FLOAT)iExponent);
1712  }
1713  if (bHavePercentSymbol) {
1714    dbRetValue /= 100.0;
1715  }
1716  if (bNeg) {
1717    dbRetValue = -dbRetValue;
1718  }
1719  fValue = (FX_FLOAT)dbRetValue;
1720  return TRUE;
1721}
1722void FX_ParseNumString(const CFX_WideString& wsNum, CFX_WideString& wsResult) {
1723  int32_t iCount = wsNum.GetLength();
1724  const FX_WCHAR* pStr = (const FX_WCHAR*)wsNum;
1725  FX_WCHAR* pDst = wsResult.GetBuffer(iCount);
1726  int32_t nIndex = 0;
1727  FX_BOOL bMinus = FALSE;
1728  int32_t i = 0;
1729  for (i = 0; i < iCount; i++) {
1730    FX_WCHAR wc = pStr[i];
1731    if (wc == '.') {
1732      break;
1733    }
1734    if ((wc == L'0' || wc == L' ' || wc == '+') && nIndex == 0) {
1735      continue;
1736    }
1737    if (wc == '-') {
1738      pDst[nIndex++] = wc;
1739      bMinus = TRUE;
1740      continue;
1741    }
1742    if (wc == L'0' && nIndex == 1 && bMinus) {
1743      continue;
1744    }
1745    pDst[nIndex++] = wc;
1746  }
1747  if (bMinus && nIndex == 1) {
1748    pDst[nIndex++] = '0';
1749  }
1750  if (nIndex == 0) {
1751    wsResult.ReleaseBuffer(0);
1752    pDst = wsResult.GetBuffer(iCount + 1);
1753    pDst[nIndex++] = '0';
1754  }
1755  int32_t j = 0;
1756  for (j = iCount - 1; j > i; j--) {
1757    FX_WCHAR wc = pStr[j];
1758    if (wc != L'0' && wc != L' ') {
1759      break;
1760    }
1761  }
1762  if (j > i) {
1763    pDst[nIndex++] = '.';
1764    FXSYS_wcsncpy(pDst + nIndex, pStr + i + 1, j - i);
1765    nIndex += j - i;
1766  }
1767  wsResult.ReleaseBuffer(nIndex);
1768}
1769FX_BOOL CFX_FormatString::ParseNum(const CFX_WideString& wsSrcNum,
1770                                   const CFX_WideString& wsPattern,
1771                                   CFX_WideString& wsValue) {
1772  wsValue.Empty();
1773  if (wsSrcNum.IsEmpty() || wsPattern.IsEmpty()) {
1774    return FALSE;
1775  }
1776  int32_t dot_index_f = -1;
1777  FX_DWORD dwFormatStyle = 0;
1778  CFX_WideString wsNumFormat;
1779  IFX_Locale* pLocale =
1780      GetNumericFormat(wsPattern, dot_index_f, dwFormatStyle, wsNumFormat);
1781  if (!pLocale || wsNumFormat.IsEmpty()) {
1782    return FALSE;
1783  }
1784  int32_t iExponent = 0;
1785  CFX_WideString wsDotSymbol;
1786  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal, wsDotSymbol);
1787  CFX_WideString wsGroupSymbol;
1788  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping, wsGroupSymbol);
1789  int32_t iGroupLen = wsGroupSymbol.GetLength();
1790  CFX_WideString wsMinus;
1791  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinus);
1792  int32_t iMinusLen = wsMinus.GetLength();
1793  int cc = 0, ccf = 0;
1794  const FX_WCHAR* str = (const FX_WCHAR*)wsSrcNum;
1795  int len = wsSrcNum.GetLength();
1796  const FX_WCHAR* strf = (const FX_WCHAR*)wsNumFormat;
1797  int lenf = wsNumFormat.GetLength();
1798  FX_BOOL bHavePercentSymbol = FALSE;
1799  FX_BOOL bNeg = FALSE;
1800  FX_BOOL bReverseParse = FALSE;
1801  int32_t dot_index = 0;
1802  if (!FX_GetNumericDotIndex(wsSrcNum, wsDotSymbol, dot_index) &&
1803      (dwFormatStyle & FX_NUMSTYLE_DotVorv)) {
1804    bReverseParse = TRUE;
1805  }
1806  bReverseParse = FALSE;
1807  ccf = dot_index_f - 1;
1808  cc = dot_index - 1;
1809  while (ccf >= 0 && cc >= 0) {
1810    switch (strf[ccf]) {
1811      case '\'': {
1812        CFX_WideString wsLiteral = FX_GetLiteralTextReverse(strf, ccf);
1813        int32_t iLiteralLen = wsLiteral.GetLength();
1814        cc -= iLiteralLen - 1;
1815        if (cc < 0 ||
1816            FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsLiteral, iLiteralLen)) {
1817          return FALSE;
1818        }
1819        cc--;
1820        ccf--;
1821        break;
1822      }
1823      case '9':
1824        if (!FX_IsDigit(str[cc])) {
1825          return FALSE;
1826        }
1827        wsValue = CFX_WideStringC(str[cc]) + wsValue;
1828        cc--;
1829        ccf--;
1830        break;
1831      case 'z':
1832        if (FX_IsDigit(str[cc])) {
1833          wsValue = CFX_WideStringC(str[cc]) + wsValue;
1834          cc--;
1835        }
1836        ccf--;
1837        break;
1838      case 'Z':
1839        if (str[cc] != ' ') {
1840          if (FX_IsDigit(str[cc])) {
1841            wsValue = CFX_WideStringC(str[cc]) + wsValue;
1842            cc--;
1843          }
1844        } else {
1845          cc--;
1846        }
1847        ccf--;
1848        break;
1849      case 'S':
1850        if (str[cc] == '+' || str[cc] == ' ') {
1851          cc--;
1852        } else {
1853          cc -= iMinusLen - 1;
1854          if (cc < 0 ||
1855              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
1856            return FALSE;
1857          }
1858          cc--;
1859          bNeg = TRUE;
1860        }
1861        ccf--;
1862        break;
1863      case 's':
1864        if (str[cc] == '+') {
1865          cc--;
1866        } else {
1867          cc -= iMinusLen - 1;
1868          if (cc < 0 ||
1869              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
1870            return FALSE;
1871          }
1872          cc--;
1873          bNeg = TRUE;
1874        }
1875        ccf--;
1876        break;
1877      case 'E': {
1878        if (cc >= dot_index) {
1879          return FALSE;
1880        }
1881        FX_BOOL bExpSign = FALSE;
1882        while (cc >= 0) {
1883          if (str[cc] == 'E' || str[cc] == 'e') {
1884            break;
1885          }
1886          if (FX_IsDigit(str[cc])) {
1887            iExponent = iExponent + (str[cc] - '0') * 10;
1888            cc--;
1889            continue;
1890          } else if (str[cc] == '+') {
1891            cc--;
1892            continue;
1893          } else if (cc - iMinusLen + 1 > 0 &&
1894                     !FXSYS_wcsncmp(str + (cc - iMinusLen + 1),
1895                                    (const FX_WCHAR*)wsMinus, iMinusLen)) {
1896            bExpSign = TRUE;
1897            cc -= iMinusLen;
1898          } else {
1899            return FALSE;
1900          }
1901        }
1902        cc--;
1903        iExponent = bExpSign ? -iExponent : iExponent;
1904        ccf--;
1905      } break;
1906      case '$': {
1907        CFX_WideString wsSymbol;
1908        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol, wsSymbol);
1909        int32_t iSymbolLen = wsSymbol.GetLength();
1910        cc -= iSymbolLen - 1;
1911        if (cc < 0 ||
1912            FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol, iSymbolLen)) {
1913          return FALSE;
1914        }
1915        cc--;
1916        ccf--;
1917      } break;
1918      case 'r':
1919        if (ccf - 1 >= 0 && strf[ccf - 1] == 'c') {
1920          if (str[cc] == 'R' && cc - 1 >= 0 && str[cc - 1] == 'C') {
1921            bNeg = TRUE;
1922            cc -= 2;
1923          }
1924          ccf -= 2;
1925        } else {
1926          ccf--;
1927        }
1928        break;
1929      case 'R':
1930        if (ccf - 1 >= 0 && strf[ccf - 1] == 'C') {
1931          if (str[cc] == ' ') {
1932            cc++;
1933          } else if (str[cc] == 'R' && cc - 1 >= 0 && str[cc - 1] == 'C') {
1934            bNeg = TRUE;
1935            cc -= 2;
1936          }
1937          ccf -= 2;
1938        } else {
1939          ccf--;
1940        }
1941        break;
1942      case 'b':
1943        if (ccf - 1 >= 0 && strf[ccf - 1] == 'd') {
1944          if (str[cc] == 'B' && cc - 1 >= 0 && str[cc - 1] == 'D') {
1945            bNeg = TRUE;
1946            cc -= 2;
1947          }
1948          ccf -= 2;
1949        } else {
1950          ccf--;
1951        }
1952        break;
1953      case 'B':
1954        if (ccf - 1 >= 0 && strf[ccf - 1] == 'D') {
1955          if (str[cc] == ' ') {
1956            cc++;
1957          } else if (str[cc] == 'B' && cc - 1 >= 0 && str[cc - 1] == 'D') {
1958            bNeg = TRUE;
1959            cc -= 2;
1960          }
1961          ccf -= 2;
1962        } else {
1963          ccf--;
1964        }
1965        break;
1966      case '.':
1967      case 'V':
1968      case 'v':
1969        return FALSE;
1970      case '%': {
1971        CFX_WideString wsSymbol;
1972        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent, wsSymbol);
1973        int32_t iSysmbolLen = wsSymbol.GetLength();
1974        cc -= iSysmbolLen - 1;
1975        if (cc < 0 ||
1976            FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol, iSysmbolLen)) {
1977          return FALSE;
1978        }
1979        cc--;
1980        ccf--;
1981        bHavePercentSymbol = TRUE;
1982      } break;
1983      case '8':
1984        return FALSE;
1985      case ',': {
1986        if (cc >= 0) {
1987          cc -= iGroupLen - 1;
1988          if (cc >= 0 &&
1989              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsGroupSymbol,
1990                            iGroupLen) == 0) {
1991            cc--;
1992          } else {
1993            cc += iGroupLen - 1;
1994          }
1995        }
1996        ccf--;
1997      } break;
1998      case '(':
1999        if (str[cc] == L'(') {
2000          bNeg = TRUE;
2001        } else if (str[cc] != L' ') {
2002          return FALSE;
2003        }
2004        cc--;
2005        ccf--;
2006        break;
2007      case ')':
2008        if (str[cc] == L')') {
2009          bNeg = TRUE;
2010        } else if (str[cc] != L' ') {
2011          return FALSE;
2012        }
2013        cc--;
2014        ccf--;
2015        break;
2016      default:
2017        if (strf[ccf] != str[cc]) {
2018          return FALSE;
2019        }
2020        cc--;
2021        ccf--;
2022    }
2023  }
2024  if (cc >= 0) {
2025    if (str[cc] == '-') {
2026      bNeg = TRUE;
2027      cc--;
2028    }
2029    if (cc >= 0) {
2030      return FALSE;
2031    }
2032  }
2033  if (dot_index < len && (dwFormatStyle & FX_NUMSTYLE_DotVorv)) {
2034    wsValue += '.';
2035  }
2036  if (!bReverseParse) {
2037    ccf = dot_index_f + 1;
2038    cc = (dot_index == len) ? len : dot_index + 1;
2039    while (cc < len && ccf < lenf) {
2040      switch (strf[ccf]) {
2041        case '\'': {
2042          CFX_WideString wsLiteral = FX_GetLiteralText(strf, ccf, lenf);
2043          int32_t iLiteralLen = wsLiteral.GetLength();
2044          if (cc + iLiteralLen > len ||
2045              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsLiteral,
2046                            iLiteralLen)) {
2047            return FALSE;
2048          }
2049          cc += iLiteralLen;
2050          ccf++;
2051          break;
2052        }
2053        case '9':
2054          if (!FX_IsDigit(str[cc])) {
2055            return FALSE;
2056          }
2057          { wsValue += str[cc]; }
2058          cc++;
2059          ccf++;
2060          break;
2061        case 'z':
2062          if (FX_IsDigit(str[cc])) {
2063            wsValue += str[cc];
2064            cc++;
2065          }
2066          ccf++;
2067          break;
2068        case 'Z':
2069          if (str[cc] != ' ') {
2070            if (FX_IsDigit(str[cc])) {
2071              wsValue += str[cc];
2072              cc++;
2073            }
2074          } else {
2075            cc++;
2076          }
2077          ccf++;
2078          break;
2079        case 'S':
2080          if (str[cc] == '+' || str[cc] == ' ') {
2081            cc++;
2082          } else {
2083            if (cc + iMinusLen > len ||
2084                FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
2085              return FALSE;
2086            }
2087            bNeg = TRUE;
2088            cc += iMinusLen;
2089          }
2090          ccf++;
2091          break;
2092        case 's':
2093          if (str[cc] == '+') {
2094            cc++;
2095          } else {
2096            if (cc + iMinusLen > len ||
2097                FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsMinus, iMinusLen)) {
2098              return FALSE;
2099            }
2100            bNeg = TRUE;
2101            cc += iMinusLen;
2102          }
2103          ccf++;
2104          break;
2105        case 'E': {
2106          if (cc >= len || (str[cc] != 'E' && str[cc] != 'e')) {
2107            return FALSE;
2108          }
2109          FX_BOOL bExpSign = FALSE;
2110          cc++;
2111          if (cc < len) {
2112            if (str[cc] == '+') {
2113              cc++;
2114            } else if (str[cc] == '-') {
2115              bExpSign = TRUE;
2116              cc++;
2117            }
2118          }
2119          while (cc < len) {
2120            if (!FX_IsDigit(str[cc])) {
2121              break;
2122            }
2123            iExponent = iExponent * 10 + str[cc] - '0';
2124            cc++;
2125          }
2126          iExponent = bExpSign ? -iExponent : iExponent;
2127          ccf++;
2128        } break;
2129        case '$': {
2130          CFX_WideString wsSymbol;
2131          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol,
2132                                     wsSymbol);
2133          int32_t iSymbolLen = wsSymbol.GetLength();
2134          if (cc + iSymbolLen > len ||
2135              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol, iSymbolLen)) {
2136            return FALSE;
2137          }
2138          cc += iSymbolLen;
2139          ccf++;
2140        } break;
2141        case 'c':
2142          if (ccf + 1 < lenf && strf[ccf + 1] == 'r') {
2143            if (str[cc] == 'C' && cc + 1 < len && str[cc + 1] == 'R') {
2144              bNeg = TRUE;
2145              cc += 2;
2146            }
2147            ccf += 2;
2148          }
2149          break;
2150        case 'C':
2151          if (ccf + 1 < lenf && strf[ccf + 1] == 'R') {
2152            if (str[cc] == ' ') {
2153              cc++;
2154            } else if (str[cc] == 'C' && cc + 1 < len && str[cc + 1] == 'R') {
2155              bNeg = TRUE;
2156              cc += 2;
2157            }
2158            ccf += 2;
2159          }
2160          break;
2161        case 'd':
2162          if (ccf + 1 < lenf && strf[ccf + 1] == 'b') {
2163            if (str[cc] == 'D' && cc + 1 < len && str[cc + 1] == 'B') {
2164              bNeg = TRUE;
2165              cc += 2;
2166            }
2167            ccf += 2;
2168          }
2169          break;
2170        case 'D':
2171          if (ccf + 1 < lenf && strf[ccf + 1] == 'B') {
2172            if (str[cc] == ' ') {
2173              cc++;
2174            } else if (str[cc] == 'D' && cc + 1 < len && str[cc + 1] == 'B') {
2175              bNeg = TRUE;
2176              cc += 2;
2177            }
2178            ccf += 2;
2179          }
2180          break;
2181        case '.':
2182        case 'V':
2183        case 'v':
2184          return FALSE;
2185        case '%': {
2186          CFX_WideString wsSymbol;
2187          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent, wsSymbol);
2188          int32_t iSysmbolLen = wsSymbol.GetLength();
2189          if (cc + iSysmbolLen <= len &&
2190              !FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsSymbol,
2191                             iSysmbolLen)) {
2192            cc += iSysmbolLen;
2193          }
2194          ccf++;
2195          bHavePercentSymbol = TRUE;
2196        } break;
2197        case '8': {
2198          while (ccf < lenf && strf[ccf] == '8') {
2199            ccf++;
2200          }
2201          while (cc < len && FX_IsDigit(str[cc])) {
2202            wsValue += str[cc];
2203            cc++;
2204          }
2205        } break;
2206        case ',': {
2207          if (cc + iGroupLen <= len &&
2208              FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsGroupSymbol,
2209                            iGroupLen) == 0) {
2210            cc += iGroupLen;
2211          }
2212          ccf++;
2213        } break;
2214        case '(':
2215          if (str[cc] == L'(') {
2216            bNeg = TRUE;
2217          } else if (str[cc] != L' ') {
2218            return FALSE;
2219          }
2220          cc++;
2221          ccf++;
2222          break;
2223        case ')':
2224          if (str[cc] == L')') {
2225            bNeg = TRUE;
2226          } else if (str[cc] != L' ') {
2227            return FALSE;
2228          }
2229          cc++;
2230          ccf++;
2231          break;
2232        default:
2233          if (strf[ccf] != str[cc]) {
2234            return FALSE;
2235          }
2236          cc++;
2237          ccf++;
2238      }
2239    }
2240    if (cc != len) {
2241      return FALSE;
2242    }
2243  }
2244  if (iExponent || bHavePercentSymbol) {
2245    CFX_Decimal decimal = CFX_Decimal(wsValue);
2246    if (iExponent) {
2247      decimal = decimal * CFX_Decimal(FXSYS_pow(10, (FX_FLOAT)iExponent));
2248    }
2249    if (bHavePercentSymbol) {
2250      decimal = decimal / CFX_Decimal(100);
2251    }
2252    wsValue = decimal;
2253  }
2254  if (bNeg) {
2255    wsValue = CFX_WideStringC('-') + wsValue;
2256  }
2257  return TRUE;
2258}
2259FX_DATETIMETYPE CFX_FormatString::GetDateTimeFormat(
2260    const CFX_WideString& wsPattern,
2261    IFX_Locale*& pLocale,
2262    CFX_WideString& wsDatePattern,
2263    CFX_WideString& wsTimePattern) {
2264  pLocale = NULL;
2265  CFX_WideString wsTempPattern;
2266  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
2267  int32_t ccf = 0;
2268  int32_t iLenf = wsPattern.GetLength();
2269  const FX_WCHAR* pStr = (const FX_WCHAR*)wsPattern;
2270  int32_t iFindCategory = 0;
2271  FX_BOOL bBraceOpen = FALSE;
2272  while (ccf < iLenf) {
2273    if (pStr[ccf] == '\'') {
2274      int32_t iCurChar = ccf;
2275      FX_GetLiteralText(pStr, ccf, iLenf);
2276      wsTempPattern += CFX_WideStringC(pStr + iCurChar, ccf - iCurChar + 1);
2277    } else if (!bBraceOpen && iFindCategory != 3 &&
2278               FX_Local_Find(gs_wsConstChars, pStr[ccf]) < 0) {
2279      CFX_WideString wsCategory(pStr[ccf]);
2280      ccf++;
2281      while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
2282             pStr[ccf] != '(') {
2283        if (pStr[ccf] == 'T') {
2284          wsDatePattern = wsPattern.Left(ccf);
2285          wsTimePattern = wsPattern.Right(wsPattern.GetLength() - ccf);
2286          wsTimePattern.SetAt(0, ' ');
2287          if (!pLocale) {
2288            pLocale = m_pLocaleMgr->GetDefLocale();
2289          }
2290          return FX_DATETIMETYPE_DateTime;
2291        }
2292        wsCategory += pStr[ccf];
2293        ccf++;
2294      }
2295      if (!(iFindCategory & 1) && wsCategory == FX_WSTRC(L"date")) {
2296        iFindCategory |= 1;
2297        eCategory = FX_LOCALECATEGORY_Date;
2298        if (iFindCategory & 2) {
2299          iFindCategory = 4;
2300        }
2301      } else if (!(iFindCategory & 2) && wsCategory == FX_WSTRC(L"time")) {
2302        iFindCategory |= 2;
2303        eCategory = FX_LOCALECATEGORY_Time;
2304      } else if (wsCategory == FX_WSTRC(L"datetime")) {
2305        iFindCategory = 3;
2306        eCategory = FX_LOCALECATEGORY_DateTime;
2307      } else {
2308        continue;
2309      }
2310      while (ccf < iLenf) {
2311        if (pStr[ccf] == '(') {
2312          ccf++;
2313          CFX_WideString wsLCID;
2314          while (ccf < iLenf && pStr[ccf] != ')') {
2315            wsLCID += pStr[ccf++];
2316          }
2317          pLocale = GetPatternLocale(wsLCID);
2318        } else if (pStr[ccf] == '{') {
2319          bBraceOpen = TRUE;
2320          break;
2321        } else if (pStr[ccf] == '.') {
2322          CFX_WideString wsSubCategory;
2323          ccf++;
2324          while (ccf < iLenf && pStr[ccf] != '(' && pStr[ccf] != '{') {
2325            wsSubCategory += pStr[ccf++];
2326          }
2327          FX_DWORD dwSubHash =
2328              FX_HashCode_String_GetW(wsSubCategory, wsSubCategory.GetLength());
2329          FX_LOCALEDATETIMESUBCATEGORY eSubCategory =
2330              FX_LOCALEDATETIMESUBCATEGORY_Medium;
2331          for (int32_t i = 0; i < g_iFXLocaleDateTimeSubCatCount; i++) {
2332            if (g_FXLocaleDateTimeSubCatData[i].uHash == dwSubHash) {
2333              eSubCategory =
2334                  (FX_LOCALEDATETIMESUBCATEGORY)g_FXLocaleDateTimeSubCatData[i]
2335                      .eSubCategory;
2336              break;
2337            }
2338          }
2339          if (!pLocale) {
2340            pLocale = m_pLocaleMgr->GetDefLocale();
2341          }
2342          FXSYS_assert(pLocale != NULL);
2343          switch (eCategory) {
2344            case FX_LOCALECATEGORY_Date:
2345              pLocale->GetDatePattern(eSubCategory, wsDatePattern);
2346              wsDatePattern = wsTempPattern + wsDatePattern;
2347              break;
2348            case FX_LOCALECATEGORY_Time:
2349              pLocale->GetTimePattern(eSubCategory, wsTimePattern);
2350              wsTimePattern = wsTempPattern + wsTimePattern;
2351              break;
2352            case FX_LOCALECATEGORY_DateTime:
2353              pLocale->GetDatePattern(eSubCategory, wsDatePattern);
2354              wsDatePattern = wsTempPattern + wsDatePattern;
2355              pLocale->GetTimePattern(eSubCategory, wsTimePattern);
2356              break;
2357            default:
2358              break;
2359          }
2360          wsTempPattern.Empty();
2361          continue;
2362        }
2363        ccf++;
2364      }
2365    } else if (pStr[ccf] == '}') {
2366      bBraceOpen = FALSE;
2367      if (!wsTempPattern.IsEmpty()) {
2368        if (eCategory == FX_LOCALECATEGORY_Time) {
2369          wsTimePattern = wsTempPattern;
2370        } else if (eCategory == FX_LOCALECATEGORY_Date) {
2371          wsDatePattern = wsTempPattern;
2372        }
2373        wsTempPattern.Empty();
2374      }
2375    } else {
2376      wsTempPattern += pStr[ccf];
2377    }
2378    ccf++;
2379  }
2380  if (!wsTempPattern.IsEmpty()) {
2381    if (eCategory == FX_LOCALECATEGORY_Date) {
2382      wsDatePattern += wsTempPattern;
2383    } else {
2384      wsTimePattern += wsTempPattern;
2385    }
2386  }
2387  if (!pLocale) {
2388    pLocale = m_pLocaleMgr->GetDefLocale();
2389  }
2390  if (!iFindCategory) {
2391    wsTimePattern.Empty();
2392    wsDatePattern = wsPattern;
2393  }
2394  return (FX_DATETIMETYPE)iFindCategory;
2395}
2396static FX_BOOL FX_ParseLocaleDate(const CFX_WideString& wsDate,
2397                                  const CFX_WideString& wsDatePattern,
2398                                  IFX_Locale* pLocale,
2399                                  CFX_Unitime& datetime,
2400                                  int32_t& cc) {
2401  int32_t year = 1900;
2402  int32_t month = 1;
2403  int32_t day = 1;
2404  int32_t ccf = 0;
2405  const FX_WCHAR* str = (const FX_WCHAR*)wsDate;
2406  int32_t len = wsDate.GetLength();
2407  const FX_WCHAR* strf = (const FX_WCHAR*)wsDatePattern;
2408  int32_t lenf = wsDatePattern.GetLength();
2409  while (cc < len && ccf < lenf) {
2410    if (strf[ccf] == '\'') {
2411      CFX_WideString wsLiteral = FX_GetLiteralText(strf, ccf, lenf);
2412      int32_t iLiteralLen = wsLiteral.GetLength();
2413      if (cc + iLiteralLen > len ||
2414          FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsLiteral, iLiteralLen)) {
2415        return FALSE;
2416      }
2417      cc += iLiteralLen;
2418      ccf++;
2419      continue;
2420    } else if (FX_Local_Find(gs_wsDateSymbols, strf[ccf]) < 0) {
2421      if (strf[ccf] != str[cc]) {
2422        return FALSE;
2423      }
2424      cc++;
2425      ccf++;
2426      continue;
2427    }
2428    FX_DWORD dwSymbolNum = 1;
2429    FX_DWORD dwSymbol = strf[ccf++];
2430    while (ccf < lenf && strf[ccf] == dwSymbol) {
2431      ccf++;
2432      dwSymbolNum++;
2433    }
2434    dwSymbol = (dwSymbol << 8) | (dwSymbolNum + '0');
2435    if (dwSymbol == FXBSTR_ID(0, 0, 'D', '1')) {
2436      if (!FX_IsDigit(str[cc])) {
2437        return FALSE;
2438      }
2439      day = str[cc++] - '0';
2440      if (cc < len && FX_IsDigit(str[cc])) {
2441        day = day * 10 + str[cc++] - '0';
2442      }
2443    } else if (dwSymbol == FXBSTR_ID(0, 0, 'D', '2')) {
2444      if (!FX_IsDigit(str[cc])) {
2445        return FALSE;
2446      }
2447      day = str[cc++] - '0';
2448      if (cc < len) {
2449        day = day * 10 + str[cc++] - '0';
2450      }
2451    } else if (dwSymbol == FXBSTR_ID(0, 0, 'J', '1')) {
2452      int i = 0;
2453      while (cc < len && i < 3 && FX_IsDigit(str[cc])) {
2454        cc++;
2455        i++;
2456      }
2457    } else if (dwSymbol == FXBSTR_ID(0, 0, 'J', '3')) {
2458      cc += 3;
2459    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '1')) {
2460      if (!FX_IsDigit(str[cc])) {
2461        return FALSE;
2462      }
2463      month = str[cc++] - '0';
2464      if (cc < len && FX_IsDigit(str[cc])) {
2465        month = month * 10 + str[cc++] - '0';
2466      }
2467    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '2')) {
2468      if (!FX_IsDigit(str[cc])) {
2469        return FALSE;
2470      }
2471      month = str[cc++] - '0';
2472      if (cc < len) {
2473        month = month * 10 + str[cc++] - '0';
2474      }
2475    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '3')) {
2476      CFX_WideString wsMonthNameAbbr;
2477      FX_WORD i = 0;
2478      for (; i < 12; i++) {
2479        pLocale->GetMonthName(i, wsMonthNameAbbr, TRUE);
2480        if (wsMonthNameAbbr.IsEmpty()) {
2481          continue;
2482        }
2483        if (!FXSYS_wcsncmp((const FX_WCHAR*)wsMonthNameAbbr, str + cc,
2484                           wsMonthNameAbbr.GetLength())) {
2485          break;
2486        }
2487      }
2488      if (i < 12) {
2489        cc += wsMonthNameAbbr.GetLength();
2490        month = i + 1;
2491      }
2492    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '4')) {
2493      CFX_WideString wsMonthName;
2494      FX_WORD i = 0;
2495      for (; i < 12; i++) {
2496        pLocale->GetMonthName(i, wsMonthName, FALSE);
2497        if (wsMonthName.IsEmpty()) {
2498          continue;
2499        }
2500        if (!FXSYS_wcsncmp((const FX_WCHAR*)wsMonthName, str + cc,
2501                           wsMonthName.GetLength())) {
2502          break;
2503        }
2504      }
2505      if (i < 12) {
2506        cc += wsMonthName.GetLength();
2507        month = i + 1;
2508      }
2509    } else if (dwSymbol == FXBSTR_ID(0, 0, 'E', '1')) {
2510      cc += 1;
2511    } else if (dwSymbol == FXBSTR_ID(0, 0, 'E', '3')) {
2512      CFX_WideString wsDayNameAbbr;
2513      FX_WORD i = 0;
2514      for (; i < 7; i++) {
2515        pLocale->GetDayName(i, wsDayNameAbbr, TRUE);
2516        if (wsDayNameAbbr.IsEmpty()) {
2517          continue;
2518        }
2519        if (!FXSYS_wcsncmp((const FX_WCHAR*)wsDayNameAbbr, str + cc,
2520                           wsDayNameAbbr.GetLength())) {
2521          break;
2522        }
2523      }
2524      if (i < 12) {
2525        cc += wsDayNameAbbr.GetLength();
2526      }
2527    } else if (dwSymbol == FXBSTR_ID(0, 0, 'E', '4')) {
2528      CFX_WideString wsDayName;
2529      int32_t i = 0;
2530      for (; i < 7; i++) {
2531        pLocale->GetDayName(i, wsDayName, FALSE);
2532        if (wsDayName == L"") {
2533          continue;
2534        }
2535        if (!FXSYS_wcsncmp((const FX_WCHAR*)wsDayName, str + cc,
2536                           wsDayName.GetLength())) {
2537          break;
2538        }
2539      }
2540      if (i < 12) {
2541        cc += wsDayName.GetLength();
2542      }
2543    } else if (dwSymbol == FXBSTR_ID(0, 0, 'e', '1')) {
2544      cc += 1;
2545    } else if (dwSymbol == FXBSTR_ID(0, 0, 'G', '1')) {
2546      cc += 2;
2547    } else if (dwSymbol == FXBSTR_ID(0, 0, 'Y', '2')) {
2548      if (cc + 2 > len) {
2549        return FALSE;
2550      }
2551      if (!FX_IsDigit(str[cc])) {
2552        return FALSE;
2553      }
2554      year = str[cc++] - '0';
2555      if (cc >= len || !FX_IsDigit(str[cc])) {
2556        return FALSE;
2557      }
2558      year = year * 10 + str[cc++] - '0';
2559      if (year <= 29) {
2560        year += 2000;
2561      } else {
2562        year += 1900;
2563      }
2564    } else if (dwSymbol == FXBSTR_ID(0, 0, 'Y', '4')) {
2565      int i = 0;
2566      year = 0;
2567      if (cc + 4 > len) {
2568        return FALSE;
2569      }
2570      while (i < 4) {
2571        if (!FX_IsDigit(str[cc])) {
2572          return FALSE;
2573        }
2574        year = year * 10 + str[cc] - '0';
2575        cc++;
2576        i++;
2577      }
2578    } else if (dwSymbol == FXBSTR_ID(0, 0, 'w', '1')) {
2579      cc += 1;
2580    } else if (dwSymbol == FXBSTR_ID(0, 0, 'W', '2')) {
2581      cc += 2;
2582    }
2583  }
2584  if (cc < len) {
2585    return FALSE;
2586  }
2587  CFX_Unitime ut;
2588  ut.Set(year, month, day);
2589  datetime = datetime + ut;
2590  return cc;
2591}
2592static void FX_ResolveZone(uint8_t& wHour,
2593                           uint8_t& wMinute,
2594                           FX_TIMEZONE tzDiff,
2595                           IFX_Locale* pLocale) {
2596  int32_t iMinuteDiff = wHour * 60 + wMinute;
2597  FX_TIMEZONE tzLocale;
2598  pLocale->GetTimeZone(tzLocale);
2599  iMinuteDiff += tzLocale.tzHour * 60 +
2600                 (tzLocale.tzHour < 0 ? -tzLocale.tzMinute : tzLocale.tzMinute);
2601  iMinuteDiff -= tzDiff.tzHour * 60 +
2602                 (tzDiff.tzHour < 0 ? -tzDiff.tzMinute : tzDiff.tzMinute);
2603  while (iMinuteDiff > 1440) {
2604    iMinuteDiff -= 1440;
2605  }
2606  while (iMinuteDiff < 0) {
2607    iMinuteDiff += 1440;
2608  }
2609  wHour = iMinuteDiff / 60;
2610  wMinute = iMinuteDiff % 60;
2611}
2612static FX_BOOL FX_ParseLocaleTime(const CFX_WideString& wsTime,
2613                                  const CFX_WideString& wsTimePattern,
2614                                  IFX_Locale* pLocale,
2615                                  CFX_Unitime& datetime,
2616                                  int32_t& cc) {
2617  uint8_t hour = 0;
2618  uint8_t minute = 0;
2619  uint8_t second = 0;
2620  FX_WORD millisecond = 0;
2621  int32_t ccf = 0;
2622  const FX_WCHAR* str = (const FX_WCHAR*)wsTime;
2623  int len = wsTime.GetLength();
2624  const FX_WCHAR* strf = (const FX_WCHAR*)wsTimePattern;
2625  int lenf = wsTimePattern.GetLength();
2626  FX_BOOL bHasA = FALSE;
2627  FX_BOOL bPM = FALSE;
2628  while (cc < len && ccf < lenf) {
2629    if (strf[ccf] == '\'') {
2630      CFX_WideString wsLiteral = FX_GetLiteralText(strf, ccf, lenf);
2631      int32_t iLiteralLen = wsLiteral.GetLength();
2632      if (cc + iLiteralLen > len ||
2633          FXSYS_wcsncmp(str + cc, (const FX_WCHAR*)wsLiteral, iLiteralLen)) {
2634        return FALSE;
2635      }
2636      cc += iLiteralLen;
2637      ccf++;
2638      continue;
2639    } else if (FX_Local_Find(gs_wsTimeSymbols, strf[ccf]) == -1) {
2640      if (strf[ccf] != str[cc]) {
2641        return FALSE;
2642      }
2643      cc++;
2644      ccf++;
2645      continue;
2646    }
2647    FX_DWORD dwSymbolNum = 1;
2648    FX_DWORD dwSymbol = strf[ccf++];
2649    while (ccf < lenf && strf[ccf] == dwSymbol) {
2650      ccf++;
2651      dwSymbolNum++;
2652    }
2653    dwSymbol = (dwSymbol << 8) | (dwSymbolNum + '0');
2654    if (dwSymbol == FXBSTR_ID(0, 0, 'k', '1') ||
2655        dwSymbol == FXBSTR_ID(0, 0, 'H', '1') ||
2656        dwSymbol == FXBSTR_ID(0, 0, 'h', '1') ||
2657        dwSymbol == FXBSTR_ID(0, 0, 'K', '1')) {
2658      if (!FX_IsDigit(str[cc])) {
2659        return FALSE;
2660      }
2661      hour = str[cc++] - '0';
2662      if (cc < len && FX_IsDigit(str[cc])) {
2663        hour = hour * 10 + str[cc++] - '0';
2664      }
2665      if (dwSymbol == FXBSTR_ID(0, 0, 'K', '1') && hour == 24) {
2666        hour = 0;
2667      }
2668    } else if (dwSymbol == FXBSTR_ID(0, 0, 'k', '2') ||
2669               dwSymbol == FXBSTR_ID(0, 0, 'H', '2') ||
2670               dwSymbol == FXBSTR_ID(0, 0, 'h', '2') ||
2671               dwSymbol == FXBSTR_ID(0, 0, 'K', '2')) {
2672      if (!FX_IsDigit(str[cc])) {
2673        return FALSE;
2674      }
2675      hour = str[cc++] - '0';
2676      if (cc >= len) {
2677        return FALSE;
2678      }
2679      if (!FX_IsDigit(str[cc])) {
2680        return FALSE;
2681      }
2682      hour = hour * 10 + str[cc++] - '0';
2683      if (dwSymbol == FXBSTR_ID(0, 0, 'K', '2') && hour == 24) {
2684        hour = 0;
2685      }
2686    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '1')) {
2687      if (!FX_IsDigit(str[cc])) {
2688        return FALSE;
2689      }
2690      minute = str[cc++] - '0';
2691      if (cc < len && FX_IsDigit(str[cc])) {
2692        minute = minute * 10 + str[cc++] - '0';
2693      }
2694    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '2')) {
2695      if (!FX_IsDigit(str[cc])) {
2696        return FALSE;
2697      }
2698      minute = str[cc++] - '0';
2699      if (cc >= len) {
2700        return FALSE;
2701      }
2702      if (!FX_IsDigit(str[cc])) {
2703        return FALSE;
2704      }
2705      minute = minute * 10 + str[cc++] - '0';
2706    } else if (dwSymbol == FXBSTR_ID(0, 0, 'S', '1')) {
2707      if (!FX_IsDigit(str[cc])) {
2708        return FALSE;
2709      }
2710      second = str[cc++] - '0';
2711      if (cc < len && FX_IsDigit(str[cc])) {
2712        second = second * 10 + str[cc++] - '0';
2713      }
2714    } else if (dwSymbol == FXBSTR_ID(0, 0, 'S', '2')) {
2715      if (!FX_IsDigit(str[cc])) {
2716        return FALSE;
2717      }
2718      second = str[cc++] - '0';
2719      if (cc >= len) {
2720        return FALSE;
2721      }
2722      if (!FX_IsDigit(str[cc])) {
2723        return FALSE;
2724      }
2725      second = second * 10 + str[cc++] - '0';
2726    } else if (dwSymbol == FXBSTR_ID(0, 0, 'F', '3')) {
2727      if (cc + 3 >= len) {
2728        return FALSE;
2729      }
2730      int i = 0;
2731      while (i < 3) {
2732        if (!FX_IsDigit(str[cc])) {
2733          return FALSE;
2734        }
2735        millisecond = millisecond * 10 + str[cc++] - '0';
2736        i++;
2737      }
2738    } else if (dwSymbol == FXBSTR_ID(0, 0, 'A', '1')) {
2739      CFX_WideString wsAM;
2740      pLocale->GetMeridiemName(wsAM, TRUE);
2741      CFX_WideString wsPM;
2742      pLocale->GetMeridiemName(wsPM, FALSE);
2743      if ((cc + wsAM.GetLength() <= len) &&
2744          (CFX_WideStringC(str + cc, wsAM.GetLength()) == wsAM)) {
2745        cc += wsAM.GetLength();
2746        bHasA = TRUE;
2747      } else if ((cc + wsPM.GetLength() <= len) &&
2748                 (CFX_WideStringC(str + cc, wsPM.GetLength()) == wsPM)) {
2749        cc += wsPM.GetLength();
2750        bHasA = TRUE;
2751        bPM = TRUE;
2752      }
2753    } else if (dwSymbol == FXBSTR_ID(0, 0, 'Z', '1')) {
2754      if (cc + 3 > len) {
2755        continue;
2756      }
2757      FX_DWORD dwHash = str[cc++];
2758      dwHash = (dwHash << 8) | str[cc++];
2759      dwHash = (dwHash << 8) | str[cc++];
2760      if (dwHash == FXBSTR_ID(0, 'G', 'M', 'T')) {
2761        FX_TIMEZONE tzDiff;
2762        tzDiff.tzHour = 0;
2763        tzDiff.tzMinute = 0;
2764        if (cc < len && (str[cc] == '-' || str[cc] == '+')) {
2765          cc += FX_ParseTimeZone(str + cc, len - cc, tzDiff);
2766        }
2767        FX_ResolveZone(hour, minute, tzDiff, pLocale);
2768      } else {
2769        FX_LPCLOCALETIMEZONEINFO pTimeZoneInfo = NULL;
2770        int32_t iStart = 0, iEnd = g_iFXLocaleTimeZoneCount - 1;
2771        do {
2772          int32_t iMid = (iStart + iEnd) / 2;
2773          FX_LPCLOCALETIMEZONEINFO pInfo = g_FXLocaleTimeZoneData + iMid;
2774          if (dwHash == pInfo->uHash) {
2775            pTimeZoneInfo = pInfo;
2776            break;
2777          } else if (dwHash < pInfo->uHash) {
2778            iEnd = iMid - 1;
2779          } else {
2780            iStart = iMid + 1;
2781          }
2782        } while (iStart <= iEnd);
2783        if (pTimeZoneInfo) {
2784          hour += pTimeZoneInfo->iHour;
2785          minute += pTimeZoneInfo->iHour > 0 ? pTimeZoneInfo->iMinute
2786                                             : -pTimeZoneInfo->iMinute;
2787        }
2788      }
2789    } else if (dwSymbol == FXBSTR_ID(0, 0, 'z', '1')) {
2790      if (str[cc] != 'Z') {
2791        FX_TIMEZONE tzDiff;
2792        cc += FX_ParseTimeZone(str + cc, len - cc, tzDiff);
2793        FX_ResolveZone(hour, minute, tzDiff, pLocale);
2794      } else {
2795        cc++;
2796      }
2797    }
2798  }
2799  if (bHasA) {
2800    if (bPM) {
2801      hour += 12;
2802      if (hour == 24) {
2803        hour = 12;
2804      }
2805    } else {
2806      if (hour == 12) {
2807        hour = 0;
2808      }
2809    }
2810  }
2811  CFX_Unitime ut;
2812  ut.Set(0, 0, 0, hour, minute, second, millisecond);
2813  datetime = datetime + ut;
2814  return cc;
2815}
2816FX_BOOL CFX_FormatString::ParseDateTime(const CFX_WideString& wsSrcDateTime,
2817                                        const CFX_WideString& wsPattern,
2818                                        FX_DATETIMETYPE eDateTimeType,
2819                                        CFX_Unitime& dtValue) {
2820  dtValue.Set(0);
2821  if (wsSrcDateTime.IsEmpty() || wsPattern.IsEmpty()) {
2822    return FALSE;
2823  }
2824  CFX_WideString wsDatePattern, wsTimePattern;
2825  IFX_Locale* pLocale = NULL;
2826  FX_DATETIMETYPE eCategory =
2827      GetDateTimeFormat(wsPattern, pLocale, wsDatePattern, wsTimePattern);
2828  if (!pLocale) {
2829    return FALSE;
2830  }
2831  if (eCategory == FX_DATETIMETYPE_Unknown) {
2832    eCategory = eDateTimeType;
2833  }
2834  if (eCategory == FX_DATETIMETYPE_Unknown) {
2835    return FALSE;
2836  }
2837  if (eCategory == FX_DATETIMETYPE_TimeDate) {
2838    int32_t iStart = 0;
2839    if (!FX_ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
2840                            iStart)) {
2841      return FALSE;
2842    }
2843    if (!FX_ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
2844                            iStart)) {
2845      return FALSE;
2846    }
2847  } else {
2848    int32_t iStart = 0;
2849    if ((eCategory & FX_DATETIMETYPE_Date) &&
2850        !FX_ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
2851                            iStart)) {
2852      return FALSE;
2853    }
2854    if ((eCategory & FX_DATETIMETYPE_Time) &&
2855        !FX_ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
2856                            iStart)) {
2857      return FALSE;
2858    }
2859  }
2860  return TRUE;
2861}
2862FX_BOOL CFX_FormatString::ParseZero(const CFX_WideString& wsSrcText,
2863                                    const CFX_WideString& wsPattern) {
2864  CFX_WideString wsTextFormat;
2865  GetTextFormat(wsPattern, FX_WSTRC(L"zero"), wsTextFormat);
2866  int32_t iText = 0, iPattern = 0;
2867  const FX_WCHAR* pStrText = (const FX_WCHAR*)wsSrcText;
2868  int32_t iLenText = wsSrcText.GetLength();
2869  const FX_WCHAR* pStrPattern = (const FX_WCHAR*)wsTextFormat;
2870  int32_t iLenPattern = wsTextFormat.GetLength();
2871  while (iPattern < iLenPattern && iText < iLenText) {
2872    if (pStrPattern[iPattern] == '\'') {
2873      CFX_WideString wsLiteral =
2874          FX_GetLiteralText(pStrPattern, iPattern, iLenPattern);
2875      int32_t iLiteralLen = wsLiteral.GetLength();
2876      if (iText + iLiteralLen > iLenText ||
2877          FXSYS_wcsncmp(pStrText + iText, (const FX_WCHAR*)wsLiteral,
2878                        iLiteralLen)) {
2879        return FALSE;
2880      }
2881      iText += iLiteralLen;
2882      iPattern++;
2883      continue;
2884    } else if (pStrPattern[iPattern] != pStrText[iText]) {
2885      return FALSE;
2886    } else {
2887      iText++;
2888      iPattern++;
2889    }
2890  }
2891  return iPattern == iLenPattern && iText == iLenText;
2892}
2893FX_BOOL CFX_FormatString::ParseNull(const CFX_WideString& wsSrcText,
2894                                    const CFX_WideString& wsPattern) {
2895  CFX_WideString wsTextFormat;
2896  GetTextFormat(wsPattern, FX_WSTRC(L"null"), wsTextFormat);
2897  int32_t iText = 0, iPattern = 0;
2898  const FX_WCHAR* pStrText = (const FX_WCHAR*)wsSrcText;
2899  int32_t iLenText = wsSrcText.GetLength();
2900  const FX_WCHAR* pStrPattern = (const FX_WCHAR*)wsTextFormat;
2901  int32_t iLenPattern = wsTextFormat.GetLength();
2902  while (iPattern < iLenPattern && iText < iLenText) {
2903    if (pStrPattern[iPattern] == '\'') {
2904      CFX_WideString wsLiteral =
2905          FX_GetLiteralText(pStrPattern, iPattern, iLenPattern);
2906      int32_t iLiteralLen = wsLiteral.GetLength();
2907      if (iText + iLiteralLen > iLenText ||
2908          FXSYS_wcsncmp(pStrText + iText, (const FX_WCHAR*)wsLiteral,
2909                        iLiteralLen)) {
2910        return FALSE;
2911      }
2912      iText += iLiteralLen;
2913      iPattern++;
2914      continue;
2915    } else if (pStrPattern[iPattern] != pStrText[iText]) {
2916      return FALSE;
2917    } else {
2918      iText++;
2919      iPattern++;
2920    }
2921  }
2922  return iPattern == iLenPattern && iText == iLenText;
2923}
2924FX_BOOL CFX_FormatString::FormatText(const CFX_WideString& wsSrcText,
2925                                     const CFX_WideString& wsPattern,
2926                                     CFX_WideString& wsOutput) {
2927  if (wsPattern.IsEmpty()) {
2928    return FALSE;
2929  }
2930  int32_t iLenText = wsSrcText.GetLength();
2931  if (iLenText == 0) {
2932    return FALSE;
2933  }
2934  CFX_WideString wsTextFormat;
2935  GetTextFormat(wsPattern, FX_WSTRC(L"text"), wsTextFormat);
2936  int32_t iText = 0, iPattern = 0;
2937  const FX_WCHAR* pStrText = (const FX_WCHAR*)wsSrcText;
2938  const FX_WCHAR* pStrPattern = (const FX_WCHAR*)wsTextFormat;
2939  int32_t iLenPattern = wsTextFormat.GetLength();
2940  while (iPattern < iLenPattern) {
2941    switch (pStrPattern[iPattern]) {
2942      case '\'': {
2943        wsOutput += FX_GetLiteralText(pStrPattern, iPattern, iLenPattern);
2944        iPattern++;
2945        break;
2946      }
2947      case 'A':
2948        if (iText >= iLenText || !FX_IsAlpha(pStrText[iText])) {
2949          return FALSE;
2950        }
2951        wsOutput += pStrText[iText++];
2952        iPattern++;
2953        break;
2954      case 'X':
2955        if (iText >= iLenText) {
2956          return FALSE;
2957        }
2958        wsOutput += pStrText[iText++];
2959        iPattern++;
2960        break;
2961      case 'O':
2962      case '0':
2963        if (iText >= iLenText ||
2964            (!FX_IsDigit(pStrText[iText]) && !FX_IsAlpha(pStrText[iText]))) {
2965          return FALSE;
2966        }
2967        wsOutput += pStrText[iText++];
2968        iPattern++;
2969        break;
2970      case '9':
2971        if (iText >= iLenText || !FX_IsDigit(pStrText[iText])) {
2972          return FALSE;
2973        }
2974        wsOutput += pStrText[iText++];
2975        iPattern++;
2976        break;
2977      default:
2978        wsOutput += pStrPattern[iPattern++];
2979        break;
2980    }
2981  }
2982  return iText == iLenText;
2983}
2984static int32_t FX_GetNumTrailingLimit(const CFX_WideString& wsFormat,
2985                                      int iDotPos,
2986                                      FX_BOOL& bTrimTailZeros) {
2987  if (iDotPos < 0) {
2988    return 0;
2989  }
2990  int32_t iCount = wsFormat.GetLength();
2991  int32_t iTreading = 0;
2992  for (iDotPos++; iDotPos < iCount; iDotPos++) {
2993    FX_WCHAR wc = wsFormat[iDotPos];
2994    if (wc == L'z' || wc == L'9' || wc == 'Z') {
2995      iTreading++;
2996      bTrimTailZeros = (wc == L'9' ? FALSE : TRUE);
2997    }
2998  }
2999  return iTreading;
3000}
3001FX_BOOL CFX_FormatString::FormatStrNum(const CFX_WideStringC& wsInputNum,
3002                                       const CFX_WideString& wsPattern,
3003                                       CFX_WideString& wsOutput) {
3004  if (wsInputNum.IsEmpty() || wsPattern.IsEmpty()) {
3005    return FALSE;
3006  }
3007  int32_t dot_index_f = -1;
3008  FX_DWORD dwNumStyle = 0;
3009  CFX_WideString wsNumFormat;
3010  IFX_Locale* pLocale =
3011      GetNumericFormat(wsPattern, dot_index_f, dwNumStyle, wsNumFormat);
3012  if (!pLocale || wsNumFormat.IsEmpty()) {
3013    return FALSE;
3014  }
3015  int32_t cc = 0, ccf = 0;
3016  const FX_WCHAR* strf = (const FX_WCHAR*)wsNumFormat;
3017  int lenf = wsNumFormat.GetLength();
3018  CFX_WideString wsSrcNum = wsInputNum;
3019  wsSrcNum.TrimLeft('0');
3020  if (wsSrcNum.IsEmpty() || wsSrcNum[0] == '.') {
3021    wsSrcNum.Insert(0, '0');
3022  }
3023  CFX_Decimal decimal = CFX_Decimal(wsSrcNum);
3024  if (dwNumStyle & FX_NUMSTYLE_Percent) {
3025    decimal = decimal * CFX_Decimal(100);
3026    wsSrcNum = decimal;
3027  }
3028  int32_t exponent = 0;
3029  if (dwNumStyle & FX_NUMSTYLE_Exponent) {
3030    int fixed_count = 0;
3031    while (ccf < dot_index_f) {
3032      switch (strf[ccf]) {
3033        case '\'':
3034          FX_GetLiteralText(strf, ccf, dot_index_f);
3035          break;
3036        case '9':
3037        case 'z':
3038        case 'Z':
3039          fixed_count++;
3040          break;
3041      }
3042      ccf++;
3043    }
3044    int threshold = 1;
3045    while (fixed_count > 1) {
3046      threshold *= 10;
3047      fixed_count--;
3048    }
3049    if (decimal != CFX_Decimal(0)) {
3050      if (decimal < CFX_Decimal(threshold)) {
3051        decimal = decimal * CFX_Decimal(10);
3052        exponent = -1;
3053        while (decimal < CFX_Decimal(threshold)) {
3054          decimal = decimal * CFX_Decimal(10);
3055          exponent -= 1;
3056        }
3057      } else if (decimal > CFX_Decimal(threshold)) {
3058        threshold *= 10;
3059        while (decimal > CFX_Decimal(threshold)) {
3060          decimal = decimal / CFX_Decimal(10);
3061          exponent += 1;
3062        }
3063      }
3064    }
3065  }
3066  FX_BOOL bTrimTailZeros = FALSE;
3067  int32_t iTreading =
3068      FX_GetNumTrailingLimit(wsNumFormat, dot_index_f, bTrimTailZeros);
3069  int32_t scale = decimal.GetScale();
3070  if (iTreading < scale) {
3071    decimal.SetScale(iTreading);
3072    wsSrcNum = decimal;
3073  }
3074  if (bTrimTailZeros && scale > 0 && iTreading > 0) {
3075    wsSrcNum.TrimRight(L"0");
3076    wsSrcNum.TrimRight(L".");
3077  }
3078  CFX_WideString wsGroupSymbol;
3079  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping, wsGroupSymbol);
3080  FX_BOOL bNeg = FALSE;
3081  if (wsSrcNum[0] == '-') {
3082    bNeg = TRUE;
3083    wsSrcNum.Delete(0, 1);
3084  }
3085  FX_BOOL bAddNeg = FALSE;
3086  const FX_WCHAR* str = (const FX_WCHAR*)wsSrcNum;
3087  int len = wsSrcNum.GetLength();
3088  int dot_index = wsSrcNum.Find('.');
3089  if (dot_index == -1) {
3090    dot_index = len;
3091  }
3092  ccf = dot_index_f - 1;
3093  cc = dot_index - 1;
3094  while (ccf >= 0) {
3095    switch (strf[ccf]) {
3096      case '9':
3097        if (cc >= 0) {
3098          if (!FX_IsDigit(str[cc])) {
3099            return FALSE;
3100          }
3101          wsOutput = CFX_WideStringC(str[cc]) + wsOutput;
3102          cc--;
3103        } else {
3104          wsOutput = CFX_WideStringC(L'0') + wsOutput;
3105        }
3106        ccf--;
3107        break;
3108      case 'z':
3109        if (cc >= 0) {
3110          if (!FX_IsDigit(str[cc])) {
3111            return FALSE;
3112          }
3113          if (str[0] != '0') {
3114            wsOutput = CFX_WideStringC(str[cc]) + wsOutput;
3115          }
3116          cc--;
3117        }
3118        ccf--;
3119        break;
3120      case 'Z':
3121        if (cc >= 0) {
3122          if (!FX_IsDigit(str[cc])) {
3123            return FALSE;
3124          }
3125          if (str[0] == '0') {
3126            wsOutput = CFX_WideStringC(L' ') + wsOutput;
3127          } else {
3128            wsOutput = CFX_WideStringC(str[cc]) + wsOutput;
3129          }
3130          cc--;
3131        } else {
3132          wsOutput = CFX_WideStringC(L' ') + wsOutput;
3133        }
3134        ccf--;
3135        break;
3136      case 'S':
3137        if (bNeg) {
3138          CFX_WideString wsMinusSymbol;
3139          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusSymbol);
3140          wsOutput = wsMinusSymbol + wsOutput;
3141          bAddNeg = TRUE;
3142        } else {
3143          wsOutput = CFX_WideStringC(L' ') + wsOutput;
3144        }
3145        ccf--;
3146        break;
3147      case 's':
3148        if (bNeg) {
3149          CFX_WideString wsMinusSymbol;
3150          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusSymbol);
3151          wsOutput = wsMinusSymbol + wsOutput;
3152          bAddNeg = TRUE;
3153        }
3154        ccf--;
3155        break;
3156      case 'E': {
3157        CFX_WideString wsExp;
3158        wsExp.Format(L"E%+d", exponent);
3159        wsOutput = wsExp + wsOutput;
3160      }
3161        ccf--;
3162        break;
3163      case '$': {
3164        CFX_WideString wsSymbol;
3165        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol, wsSymbol);
3166        wsOutput = wsSymbol + wsOutput;
3167      }
3168        ccf--;
3169        break;
3170      case 'r':
3171        if (ccf - 1 >= 0 && strf[ccf - 1] == 'c') {
3172          if (bNeg) {
3173            wsOutput = L"CR" + wsOutput;
3174          }
3175          ccf -= 2;
3176          bAddNeg = TRUE;
3177        }
3178        break;
3179      case 'R':
3180        if (ccf - 1 >= 0 && strf[ccf - 1] == 'C') {
3181          if (bNeg) {
3182            wsOutput = L"CR" + wsOutput;
3183          } else {
3184            wsOutput = L"  " + wsOutput;
3185          }
3186          ccf -= 2;
3187          bAddNeg = TRUE;
3188        }
3189        break;
3190      case 'b':
3191        if (ccf - 1 >= 0 && strf[ccf - 1] == 'd') {
3192          if (bNeg) {
3193            wsOutput = L"db" + wsOutput;
3194          }
3195          ccf -= 2;
3196          bAddNeg = TRUE;
3197        }
3198        break;
3199      case 'B':
3200        if (ccf - 1 >= 0 && strf[ccf - 1] == 'D') {
3201          if (bNeg) {
3202            wsOutput = L"DB" + wsOutput;
3203          } else {
3204            wsOutput = L"  " + wsOutput;
3205          }
3206          ccf -= 2;
3207          bAddNeg = TRUE;
3208        }
3209        break;
3210      case '%': {
3211        CFX_WideString wsSymbol;
3212        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent, wsSymbol);
3213        wsOutput = wsSymbol + wsOutput;
3214      }
3215        ccf--;
3216        break;
3217      case ',':
3218        if (cc >= 0) {
3219          wsOutput = wsGroupSymbol + wsOutput;
3220        }
3221        ccf--;
3222        break;
3223      case '(':
3224        if (bNeg) {
3225          wsOutput = L"(" + wsOutput;
3226        } else {
3227          wsOutput = L" " + wsOutput;
3228        }
3229        bAddNeg = TRUE;
3230        ccf--;
3231        break;
3232      case ')':
3233        if (bNeg) {
3234          wsOutput = L")" + wsOutput;
3235        } else {
3236          wsOutput = L" " + wsOutput;
3237        }
3238        ccf--;
3239        break;
3240      case '\'':
3241        wsOutput = FX_GetLiteralTextReverse(strf, ccf) + wsOutput;
3242        ccf--;
3243        break;
3244      default:
3245        wsOutput = CFX_WideStringC(strf[ccf]) + wsOutput;
3246        ccf--;
3247    }
3248  }
3249  if (cc >= 0) {
3250    int nPos = dot_index % 3;
3251    wsOutput.Empty();
3252    for (int32_t i = 0; i < dot_index; i++) {
3253      if (i % 3 == nPos && i != 0) {
3254        wsOutput += wsGroupSymbol;
3255      }
3256      wsOutput += wsSrcNum[i];
3257    }
3258    if (dot_index < len) {
3259      CFX_WideString wsSymbol;
3260      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal, wsSymbol);
3261      wsOutput += wsSymbol;
3262      wsOutput += wsSrcNum.Right(len - dot_index - 1);
3263    }
3264    if (bNeg) {
3265      CFX_WideString wsMinusymbol;
3266      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusymbol);
3267      wsOutput = wsMinusymbol + wsOutput;
3268    }
3269    return FALSE;
3270  }
3271  if (dot_index_f == wsNumFormat.GetLength()) {
3272    if (!bAddNeg && bNeg) {
3273      CFX_WideString wsMinusymbol;
3274      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusymbol);
3275      wsOutput = wsMinusymbol + wsOutput;
3276    }
3277    return TRUE;
3278  }
3279  CFX_WideString wsDotSymbol;
3280  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal, wsDotSymbol);
3281  if (strf[dot_index_f] == 'V') {
3282    wsOutput += wsDotSymbol;
3283  } else if (strf[dot_index_f] == '.') {
3284    if (dot_index < len) {
3285      wsOutput += wsDotSymbol;
3286    } else {
3287      if (strf[dot_index_f + 1] == '9' || strf[dot_index_f + 1] == 'Z') {
3288        wsOutput += wsDotSymbol;
3289      }
3290    }
3291  }
3292  ccf = dot_index_f + 1;
3293  cc = dot_index + 1;
3294  while (ccf < lenf) {
3295    switch (strf[ccf]) {
3296      case '\'':
3297        wsOutput += FX_GetLiteralText(strf, ccf, lenf);
3298        ccf++;
3299        break;
3300      case '9':
3301        if (cc < len) {
3302          if (!FX_IsDigit(str[cc])) {
3303            return FALSE;
3304          }
3305          wsOutput += str[cc];
3306          cc++;
3307        } else {
3308          wsOutput += L'0';
3309        }
3310        ccf++;
3311        break;
3312      case 'z':
3313        if (cc < len) {
3314          if (!FX_IsDigit(str[cc])) {
3315            return FALSE;
3316          }
3317          wsOutput += str[cc];
3318          cc++;
3319        }
3320        ccf++;
3321        break;
3322      case 'Z':
3323        if (cc < len) {
3324          if (!FX_IsDigit(str[cc])) {
3325            return FALSE;
3326          }
3327          wsOutput += str[cc];
3328          cc++;
3329        } else {
3330          wsOutput += L'0';
3331        }
3332        ccf++;
3333        break;
3334      case 'E': {
3335        CFX_WideString wsExp;
3336        wsExp.Format(L"E%+d", exponent);
3337        wsOutput += wsExp;
3338      }
3339        ccf++;
3340        break;
3341      case '$': {
3342        CFX_WideString wsSymbol;
3343        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol, wsSymbol);
3344        wsOutput += wsSymbol;
3345      }
3346        ccf++;
3347        break;
3348      case 'c':
3349        if (ccf + 1 < lenf && strf[ccf + 1] == 'r') {
3350          if (bNeg) {
3351            wsOutput += L"CR";
3352          }
3353          ccf += 2;
3354          bAddNeg = TRUE;
3355        }
3356        break;
3357      case 'C':
3358        if (ccf + 1 < lenf && strf[ccf + 1] == 'R') {
3359          if (bNeg) {
3360            wsOutput += L"CR";
3361          } else {
3362            wsOutput += L"  ";
3363          }
3364          ccf += 2;
3365          bAddNeg = TRUE;
3366        }
3367        break;
3368      case 'd':
3369        if (ccf + 1 < lenf && strf[ccf + 1] == 'b') {
3370          if (bNeg) {
3371            wsOutput += L"db";
3372          }
3373          ccf += 2;
3374          bAddNeg = TRUE;
3375        }
3376        break;
3377      case 'D':
3378        if (ccf + 1 < lenf && strf[ccf + 1] == 'B') {
3379          if (bNeg) {
3380            wsOutput += L"DB";
3381          } else {
3382            wsOutput += L"  ";
3383          }
3384          ccf += 2;
3385          bAddNeg = TRUE;
3386        }
3387        break;
3388      case '%': {
3389        CFX_WideString wsSymbol;
3390        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent, wsSymbol);
3391        wsOutput += wsSymbol;
3392      }
3393        ccf++;
3394        break;
3395      case '8': {
3396        while (ccf < lenf && strf[ccf] == '8') {
3397          ccf++;
3398        }
3399        while (cc < len && FX_IsDigit(str[cc])) {
3400          wsOutput += str[cc];
3401          cc++;
3402        }
3403      } break;
3404      case ',':
3405        wsOutput += wsGroupSymbol;
3406        ccf++;
3407        break;
3408      case '(':
3409        if (bNeg) {
3410          wsOutput += '(';
3411        } else {
3412          wsOutput += ' ';
3413        }
3414        bAddNeg = TRUE;
3415        ccf++;
3416        break;
3417      case ')':
3418        if (bNeg) {
3419          wsOutput += ')';
3420        } else {
3421          wsOutput += ' ';
3422        }
3423        ccf++;
3424        break;
3425      default:
3426        ccf++;
3427    }
3428  }
3429  if (!bAddNeg && bNeg) {
3430    CFX_WideString wsMinusymbol;
3431    pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusymbol);
3432    wsOutput =
3433        wsMinusymbol + wsOutput[0] + wsOutput.Mid(1, wsOutput.GetLength() - 1);
3434  }
3435  return TRUE;
3436}
3437FX_BOOL CFX_FormatString::FormatLCNumeric(CFX_LCNumeric& lcNum,
3438                                          const CFX_WideString& wsPattern,
3439                                          CFX_WideString& wsOutput) {
3440  int32_t dot_index_f = -1;
3441  FX_DWORD dwNumStyle = 0;
3442  CFX_WideString wsNumFormat;
3443  IFX_Locale* pLocale =
3444      GetNumericFormat(wsPattern, dot_index_f, dwNumStyle, wsNumFormat);
3445  if (!pLocale || wsNumFormat.IsEmpty()) {
3446    return FALSE;
3447  }
3448  int32_t cc = 0, ccf = 0;
3449  const FX_WCHAR* strf = (const FX_WCHAR*)wsNumFormat;
3450  int lenf = wsNumFormat.GetLength();
3451  double dbOrgRaw = lcNum.GetDouble();
3452  double dbRetValue = dbOrgRaw;
3453  if (dwNumStyle & FX_NUMSTYLE_Percent) {
3454    dbRetValue *= 100;
3455  }
3456  int32_t exponent = 0;
3457  if (dwNumStyle & FX_NUMSTYLE_Exponent) {
3458    int fixed_count = 0;
3459    while (ccf < dot_index_f) {
3460      switch (strf[ccf]) {
3461        case '\'':
3462          FX_GetLiteralText(strf, ccf, dot_index_f);
3463          break;
3464        case '9':
3465        case 'z':
3466        case 'Z':
3467          fixed_count++;
3468          break;
3469      }
3470      ccf++;
3471    }
3472    int threshold = 1;
3473    while (fixed_count > 1) {
3474      threshold *= 10;
3475      fixed_count--;
3476    }
3477    if (dbRetValue != 0) {
3478      if (dbRetValue < threshold) {
3479        dbRetValue *= 10;
3480        exponent = -1;
3481        while (dbRetValue < threshold) {
3482          dbRetValue *= 10;
3483          exponent -= 1;
3484        }
3485      } else if (dbRetValue > threshold) {
3486        threshold *= 10;
3487        while (dbRetValue > threshold) {
3488          dbRetValue /= 10;
3489          exponent += 1;
3490        }
3491      }
3492    }
3493  }
3494  if (dwNumStyle & (FX_NUMSTYLE_Percent | FX_NUMSTYLE_Exponent)) {
3495    lcNum = CFX_LCNumeric(dbRetValue);
3496  }
3497  FX_BOOL bTrimTailZeros = FALSE;
3498  int32_t iTreading =
3499      FX_GetNumTrailingLimit(wsNumFormat, dot_index_f, bTrimTailZeros);
3500  CFX_WideString wsNumeric = lcNum.ToString(iTreading, bTrimTailZeros);
3501  if (wsNumeric.IsEmpty()) {
3502    return FALSE;
3503  }
3504  CFX_WideString wsGroupSymbol;
3505  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping, wsGroupSymbol);
3506  FX_BOOL bNeg = FALSE;
3507  if (wsNumeric[0] == '-') {
3508    bNeg = TRUE;
3509    wsNumeric.Delete(0, 1);
3510  }
3511  FX_BOOL bAddNeg = FALSE;
3512  const FX_WCHAR* str = (const FX_WCHAR*)wsNumeric;
3513  int len = wsNumeric.GetLength();
3514  int dot_index = wsNumeric.Find('.');
3515  if (dot_index == -1) {
3516    dot_index = len;
3517  }
3518  ccf = dot_index_f - 1;
3519  cc = dot_index - 1;
3520  while (ccf >= 0) {
3521    switch (strf[ccf]) {
3522      case '9':
3523        if (cc >= 0) {
3524          wsOutput = CFX_WideStringC(str[cc]) + wsOutput;
3525          cc--;
3526        } else {
3527          wsOutput = CFX_WideStringC(L'0') + wsOutput;
3528        }
3529        ccf--;
3530        break;
3531      case 'z':
3532        if (cc >= 0) {
3533          if (lcNum.m_Integral != 0) {
3534            wsOutput = CFX_WideStringC(str[cc]) + wsOutput;
3535          }
3536          cc--;
3537        }
3538        ccf--;
3539        break;
3540      case 'Z':
3541        if (cc >= 0) {
3542          if (lcNum.m_Integral == 0) {
3543            wsOutput = CFX_WideStringC(L' ') + wsOutput;
3544          } else {
3545            wsOutput = CFX_WideStringC(str[cc]) + wsOutput;
3546          }
3547          cc--;
3548        } else {
3549          wsOutput = CFX_WideStringC(L' ') + wsOutput;
3550        }
3551        ccf--;
3552        break;
3553      case 'S':
3554        if (bNeg) {
3555          CFX_WideString wsMinusSymbol;
3556          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusSymbol);
3557          wsOutput = wsMinusSymbol + wsOutput;
3558          bAddNeg = TRUE;
3559        } else {
3560          wsOutput = CFX_WideStringC(L' ') + wsOutput;
3561        }
3562        ccf--;
3563        break;
3564      case 's':
3565        if (bNeg) {
3566          CFX_WideString wsMinusSymbol;
3567          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusSymbol);
3568          wsOutput = wsMinusSymbol + wsOutput;
3569          bAddNeg = TRUE;
3570        }
3571        ccf--;
3572        break;
3573      case 'E': {
3574        CFX_WideString wsExp;
3575        wsExp.Format(L"E%+d", exponent);
3576        wsOutput = wsExp + wsOutput;
3577      }
3578        ccf--;
3579        break;
3580      case '$': {
3581        CFX_WideString wsSymbol;
3582        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol, wsSymbol);
3583        wsOutput = wsSymbol + wsOutput;
3584      }
3585        ccf--;
3586        break;
3587      case 'r':
3588        if (ccf - 1 >= 0 && strf[ccf - 1] == 'c') {
3589          if (bNeg) {
3590            wsOutput = L"CR" + wsOutput;
3591          }
3592          ccf -= 2;
3593          bAddNeg = TRUE;
3594        }
3595        break;
3596      case 'R':
3597        if (ccf - 1 >= 0 && strf[ccf - 1] == 'C') {
3598          if (bNeg) {
3599            wsOutput = L"CR" + wsOutput;
3600          } else {
3601            wsOutput = L"  " + wsOutput;
3602          }
3603          ccf -= 2;
3604          bAddNeg = TRUE;
3605        }
3606        break;
3607      case 'b':
3608        if (ccf - 1 >= 0 && strf[ccf - 1] == 'd') {
3609          if (bNeg) {
3610            wsOutput = L"db" + wsOutput;
3611          }
3612          ccf -= 2;
3613          bAddNeg = TRUE;
3614        }
3615        break;
3616      case 'B':
3617        if (ccf - 1 >= 0 && strf[ccf - 1] == 'D') {
3618          if (bNeg) {
3619            wsOutput = L"DB" + wsOutput;
3620          } else {
3621            wsOutput = L"  " + wsOutput;
3622          }
3623          ccf -= 2;
3624          bAddNeg = TRUE;
3625        }
3626        break;
3627      case '%': {
3628        CFX_WideString wsSymbol;
3629        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent, wsSymbol);
3630        wsOutput = wsSymbol + wsOutput;
3631      }
3632        ccf--;
3633        break;
3634      case ',':
3635        if (cc >= 0) {
3636          wsOutput = wsGroupSymbol + wsOutput;
3637        }
3638        ccf--;
3639        break;
3640      case '(':
3641        if (bNeg) {
3642          wsOutput = L"(" + wsOutput;
3643        } else {
3644          wsOutput = L" " + wsOutput;
3645        }
3646        bAddNeg = TRUE;
3647        ccf--;
3648        break;
3649      case ')':
3650        if (bNeg) {
3651          wsOutput = L")" + wsOutput;
3652        } else {
3653          wsOutput = L" " + wsOutput;
3654        }
3655        ccf--;
3656        break;
3657      case '\'':
3658        wsOutput = FX_GetLiteralTextReverse(strf, ccf) + wsOutput;
3659        ccf--;
3660        break;
3661      default:
3662        wsOutput = CFX_WideStringC(strf[ccf]) + wsOutput;
3663        ccf--;
3664    }
3665  }
3666  if (cc >= 0) {
3667    int nPos = dot_index % 3;
3668    wsOutput.Empty();
3669    for (int32_t i = 0; i < dot_index; i++) {
3670      if (i % 3 == nPos && i != 0) {
3671        wsOutput += wsGroupSymbol;
3672      }
3673      wsOutput += wsNumeric[i];
3674    }
3675    if (dot_index < len) {
3676      CFX_WideString wsSymbol;
3677      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal, wsSymbol);
3678      wsOutput += wsSymbol;
3679      wsOutput += wsNumeric.Right(len - dot_index - 1);
3680    }
3681    if (bNeg) {
3682      CFX_WideString wsMinusymbol;
3683      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusymbol);
3684      wsOutput = wsMinusymbol + wsOutput;
3685    }
3686    return FALSE;
3687  }
3688  if (dot_index_f == wsNumFormat.GetLength()) {
3689    if (!bAddNeg && bNeg) {
3690      CFX_WideString wsMinusymbol;
3691      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusymbol);
3692      wsOutput = wsMinusymbol + wsOutput;
3693    }
3694    return TRUE;
3695  }
3696  CFX_WideString wsDotSymbol;
3697  pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal, wsDotSymbol);
3698  if (strf[dot_index_f] == 'V') {
3699    wsOutput += wsDotSymbol;
3700  } else if (strf[dot_index_f] == '.') {
3701    if (dot_index < len) {
3702      wsOutput += wsDotSymbol;
3703    } else {
3704      if (strf[dot_index_f + 1] == '9' || strf[dot_index_f + 1] == 'Z') {
3705        wsOutput += wsDotSymbol;
3706      }
3707    }
3708  }
3709  ccf = dot_index_f + 1;
3710  cc = dot_index + 1;
3711  while (ccf < lenf) {
3712    switch (strf[ccf]) {
3713      case '\'':
3714        wsOutput += FX_GetLiteralText(strf, ccf, lenf);
3715        ccf++;
3716        break;
3717      case '9':
3718        if (cc < len) {
3719          wsOutput += str[cc];
3720          cc++;
3721        } else {
3722          wsOutput += L'0';
3723        }
3724        ccf++;
3725        break;
3726      case 'z':
3727        if (cc < len) {
3728          wsOutput += str[cc];
3729          cc++;
3730        }
3731        ccf++;
3732        break;
3733      case 'Z':
3734        if (cc < len) {
3735          wsOutput += str[cc];
3736          cc++;
3737        } else {
3738          wsOutput += L'0';
3739        }
3740        ccf++;
3741        break;
3742      case 'E': {
3743        CFX_WideString wsExp;
3744        wsExp.Format(L"E%+d", exponent);
3745        wsOutput += wsExp;
3746      }
3747        ccf++;
3748        break;
3749      case '$': {
3750        CFX_WideString wsSymbol;
3751        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol, wsSymbol);
3752        wsOutput += wsSymbol;
3753      }
3754        ccf++;
3755        break;
3756      case 'c':
3757        if (ccf + 1 < lenf && strf[ccf + 1] == 'r') {
3758          if (bNeg) {
3759            wsOutput += L"CR";
3760          }
3761          ccf += 2;
3762          bAddNeg = TRUE;
3763        }
3764        break;
3765      case 'C':
3766        if (ccf + 1 < lenf && strf[ccf + 1] == 'R') {
3767          if (bNeg) {
3768            wsOutput += L"CR";
3769          } else {
3770            wsOutput += L"  ";
3771          }
3772          ccf += 2;
3773          bAddNeg = TRUE;
3774        }
3775        break;
3776      case 'd':
3777        if (ccf + 1 < lenf && strf[ccf + 1] == 'b') {
3778          if (bNeg) {
3779            wsOutput += L"db";
3780          }
3781          ccf += 2;
3782          bAddNeg = TRUE;
3783        }
3784        break;
3785      case 'D':
3786        if (ccf + 1 < lenf && strf[ccf + 1] == 'B') {
3787          if (bNeg) {
3788            wsOutput += L"DB";
3789          } else {
3790            wsOutput += L"  ";
3791          }
3792          ccf += 2;
3793          bAddNeg = TRUE;
3794        }
3795        break;
3796      case '%': {
3797        CFX_WideString wsSymbol;
3798        pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent, wsSymbol);
3799        wsOutput += wsSymbol;
3800      }
3801        ccf++;
3802        break;
3803      case '8': {
3804        while (ccf < lenf && strf[ccf] == '8') {
3805          ccf++;
3806        }
3807        while (cc < len && FX_IsDigit(str[cc])) {
3808          wsOutput += str[cc];
3809          cc++;
3810        }
3811      } break;
3812      case ',':
3813        wsOutput += wsGroupSymbol;
3814        ccf++;
3815        break;
3816      case '(':
3817        if (bNeg) {
3818          wsOutput += '(';
3819        } else {
3820          wsOutput += ' ';
3821        }
3822        bAddNeg = TRUE;
3823        ccf++;
3824        break;
3825      case ')':
3826        if (bNeg) {
3827          wsOutput += ')';
3828        } else {
3829          wsOutput += ' ';
3830        }
3831        ccf++;
3832        break;
3833      default:
3834        ccf++;
3835    }
3836  }
3837  if (!bAddNeg && bNeg) {
3838    CFX_WideString wsMinusymbol;
3839    pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus, wsMinusymbol);
3840    wsOutput =
3841        wsOutput[0] + wsMinusymbol + wsOutput.Mid(1, wsOutput.GetLength() - 1);
3842  }
3843  return TRUE;
3844}
3845FX_BOOL CFX_FormatString::FormatNum(const CFX_WideString& wsSrcNum,
3846                                    const CFX_WideString& wsPattern,
3847                                    CFX_WideString& wsOutput) {
3848  if (wsSrcNum.IsEmpty() || wsPattern.IsEmpty()) {
3849    return FALSE;
3850  }
3851  return FormatStrNum(wsSrcNum, wsPattern, wsOutput);
3852}
3853FX_BOOL CFX_FormatString::FormatNum(FX_FLOAT fNum,
3854                                    const CFX_WideString& wsPattern,
3855                                    CFX_WideString& wsOutput) {
3856  if (wsPattern.IsEmpty()) {
3857    return FALSE;
3858  }
3859  CFX_LCNumeric lcNum(fNum);
3860  return FormatLCNumeric(lcNum, wsPattern, wsOutput);
3861}
3862FX_BOOL FX_DateFromCanonical(const CFX_WideString& wsDate,
3863                             CFX_Unitime& datetime) {
3864  int32_t year = 1900;
3865  int32_t month = 1;
3866  int32_t day = 1;
3867  FX_WORD wYear = 0;
3868  int cc_start = 0, cc = 0;
3869  const FX_WCHAR* str = (const FX_WCHAR*)wsDate;
3870  int len = wsDate.GetLength();
3871  if (len > 10) {
3872    return FALSE;
3873  }
3874  while (cc < len && cc < 4) {
3875    if (!FX_IsDigit(str[cc])) {
3876      return FALSE;
3877    }
3878    wYear = wYear * 10 + str[cc++] - '0';
3879  }
3880  year = wYear;
3881  if (cc < 4 || wYear < 1900) {
3882    return FALSE;
3883  }
3884  if (cc < len) {
3885    if (str[cc] == '-') {
3886      cc++;
3887    }
3888    cc_start = cc;
3889    uint8_t tmpM = 0;
3890    while (cc < len && cc < cc_start + 2) {
3891      if (!FX_IsDigit(str[cc])) {
3892        return FALSE;
3893      }
3894      tmpM = tmpM * 10 + str[cc++] - '0';
3895    }
3896    month = tmpM;
3897    if (cc == cc_start + 1 || tmpM > 12 || tmpM < 1) {
3898      return FALSE;
3899    }
3900    if (cc < len) {
3901      if (str[cc] == '-') {
3902        cc++;
3903      }
3904      uint8_t tmpD = 0;
3905      cc_start = cc;
3906      while (cc < len && cc < cc_start + 2) {
3907        if (!FX_IsDigit(str[cc])) {
3908          return FALSE;
3909        }
3910        tmpD = tmpD * 10 + str[cc++] - '0';
3911      }
3912      day = tmpD;
3913      if (tmpD < 1) {
3914        return FALSE;
3915      }
3916      if ((tmpM == 1 || tmpM == 3 || tmpM == 5 || tmpM == 7 || tmpM == 8 ||
3917           tmpM == 10 || tmpM == 12) &&
3918          tmpD > 31) {
3919        return FALSE;
3920      }
3921      if ((tmpM == 4 || tmpM == 6 || tmpM == 9 || tmpM == 11) && tmpD > 30) {
3922        return FALSE;
3923      }
3924      FX_BOOL iLeapYear;
3925      if ((wYear % 4 == 0 && wYear % 100 != 0) || wYear % 400 == 0) {
3926        iLeapYear = TRUE;
3927      } else {
3928        iLeapYear = FALSE;
3929      }
3930      if ((iLeapYear && tmpM == 2 && tmpD > 29) ||
3931          (!iLeapYear && tmpM == 2 && tmpD > 28)) {
3932        return FALSE;
3933      }
3934    }
3935  }
3936  CFX_Unitime ut;
3937  ut.Set(year, month, day);
3938  datetime = datetime + ut;
3939  return TRUE;
3940}
3941FX_BOOL FX_TimeFromCanonical(const CFX_WideStringC& wsTime,
3942                             CFX_Unitime& datetime,
3943                             IFX_Locale* pLocale) {
3944  if (wsTime.GetLength() == 0) {
3945    return FALSE;
3946  }
3947  uint8_t hour = 0;
3948  uint8_t minute = 0;
3949  uint8_t second = 0;
3950  FX_WORD millisecond = 0;
3951  int cc_start = 0, cc = cc_start;
3952  const FX_WCHAR* str = (const FX_WCHAR*)wsTime.GetPtr();
3953  int len = wsTime.GetLength();
3954  while (cc < len && cc < 2) {
3955    if (!FX_IsDigit(str[cc])) {
3956      return FALSE;
3957    }
3958    hour = hour * 10 + str[cc++] - '0';
3959  }
3960  if (cc < 2 || hour >= 24) {
3961    return FALSE;
3962  }
3963  if (cc < len) {
3964    if (str[cc] == ':') {
3965      cc++;
3966    }
3967    cc_start = cc;
3968    while (cc < len && cc < cc_start + 2) {
3969      if (!FX_IsDigit(str[cc])) {
3970        return FALSE;
3971      }
3972      minute = minute * 10 + str[cc++] - '0';
3973    }
3974    if (cc == cc_start + 1 || minute >= 60) {
3975      return FALSE;
3976    }
3977    if (cc < len) {
3978      if (str[cc] == ':') {
3979        cc++;
3980      }
3981      cc_start = cc;
3982      while (cc < len && cc < cc_start + 2) {
3983        if (!FX_IsDigit(str[cc])) {
3984          return FALSE;
3985        }
3986        second = second * 10 + str[cc++] - '0';
3987      }
3988      if (cc == cc_start + 1 || second >= 60) {
3989        return FALSE;
3990      }
3991      if (cc < len) {
3992        if (str[cc] == '.') {
3993          cc++;
3994          cc_start = cc;
3995          while (cc < len && cc < cc_start + 3) {
3996            if (!FX_IsDigit(str[cc])) {
3997              return FALSE;
3998            }
3999            millisecond = millisecond * 10 + str[cc++] - '0';
4000          }
4001          if (cc < cc_start + 3 || millisecond >= 1000) {
4002            return FALSE;
4003          }
4004        }
4005        if (cc < len) {
4006          FX_TIMEZONE tzDiff;
4007          tzDiff.tzHour = 0;
4008          tzDiff.tzMinute = 0;
4009          if (str[cc] != 'Z') {
4010            cc += FX_ParseTimeZone(str + cc, len - cc, tzDiff);
4011          }
4012          FX_ResolveZone(hour, minute, tzDiff, pLocale);
4013        }
4014      }
4015    }
4016  }
4017  CFX_Unitime ut;
4018  ut.Set(0, 0, 0, hour, minute, second, millisecond);
4019  datetime = datetime + ut;
4020  return TRUE;
4021}
4022static FX_WORD FX_GetSolarMonthDays(FX_WORD year, FX_WORD month) {
4023  if (month % 2) {
4024    return 31;
4025  } else if (month == 2) {
4026    return FX_IsLeapYear(year) ? 29 : 28;
4027  }
4028  return 30;
4029}
4030static FX_WORD FX_GetWeekDay(FX_WORD year, FX_WORD month, FX_WORD day) {
4031  FX_WORD g_month_day[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
4032  FX_WORD nDays =
4033      (year - 1) % 7 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
4034  nDays += g_month_day[month - 1] + day;
4035  if (FX_IsLeapYear(year) && month > 2) {
4036    nDays++;
4037  }
4038  return nDays % 7;
4039}
4040static FX_WORD FX_GetWeekOfMonth(FX_WORD year, FX_WORD month, FX_WORD day) {
4041  FX_WORD week_day = FX_GetWeekDay(year, month, 1);
4042  FX_WORD week_index = 0;
4043  week_index += day / 7;
4044  day = day % 7;
4045  if (week_day + day > 7) {
4046    week_index++;
4047  }
4048  return week_index;
4049}
4050static FX_WORD FX_GetWeekOfYear(FX_WORD year, FX_WORD month, FX_WORD day) {
4051  FX_WORD nDays = 0;
4052  for (FX_WORD i = 1; i < month; i++) {
4053    nDays += FX_GetSolarMonthDays(year, i);
4054  }
4055  nDays += day;
4056  FX_WORD week_day = FX_GetWeekDay(year, 1, 1);
4057  FX_WORD week_index = 1;
4058  week_index += nDays / 7;
4059  nDays = nDays % 7;
4060  if (week_day + nDays > 7) {
4061    week_index++;
4062  }
4063  return week_index;
4064}
4065static FX_BOOL FX_DateFormat(const CFX_WideString& wsDatePattern,
4066                             IFX_Locale* pLocale,
4067                             const CFX_Unitime& datetime,
4068                             CFX_WideString& wsResult) {
4069  FX_BOOL bRet = TRUE;
4070  int32_t year = datetime.GetYear();
4071  uint8_t month = datetime.GetMonth();
4072  uint8_t day = datetime.GetDay();
4073  int32_t ccf = 0;
4074  const FX_WCHAR* strf = (const FX_WCHAR*)wsDatePattern;
4075  int32_t lenf = wsDatePattern.GetLength();
4076  while (ccf < lenf) {
4077    if (strf[ccf] == '\'') {
4078      wsResult += FX_GetLiteralText(strf, ccf, lenf);
4079      ccf++;
4080      continue;
4081    } else if (FX_Local_Find(gs_wsDateSymbols, strf[ccf]) < 0) {
4082      wsResult += strf[ccf++];
4083      continue;
4084    }
4085    FX_DWORD dwSymbolNum = 1;
4086    FX_DWORD dwSymbol = strf[ccf++];
4087    while (ccf < lenf && strf[ccf] == dwSymbol) {
4088      ccf++;
4089      dwSymbolNum++;
4090    }
4091    dwSymbol = (dwSymbol << 8) | (dwSymbolNum + '0');
4092    if (dwSymbol == FXBSTR_ID(0, 0, 'D', '1')) {
4093      CFX_WideString wsDay;
4094      wsDay.Format(L"%d", day);
4095      wsResult += wsDay;
4096    } else if (dwSymbol == FXBSTR_ID(0, 0, 'D', '2')) {
4097      CFX_WideString wsDay;
4098      wsDay.Format(L"%02d", day);
4099      wsResult += wsDay;
4100    } else if (dwSymbol == FXBSTR_ID(0, 0, 'J', '1')) {
4101      FX_WORD nDays = 0;
4102      for (int i = 1; i < month; i++) {
4103        nDays += FX_GetSolarMonthDays(year, i);
4104      }
4105      nDays += day;
4106      CFX_WideString wsDays;
4107      wsDays.Format(L"%d", nDays);
4108      wsResult += wsDays;
4109    } else if (dwSymbol == FXBSTR_ID(0, 0, 'J', '3')) {
4110      FX_WORD nDays = 0;
4111      for (int i = 1; i < month; i++) {
4112        nDays += FX_GetSolarMonthDays(year, i);
4113      }
4114      nDays += day;
4115      CFX_WideString wsDays;
4116      wsDays.Format(L"%03d", nDays);
4117      wsResult += wsDays;
4118    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '1')) {
4119      CFX_WideString wsMonth;
4120      wsMonth.Format(L"%d", month);
4121      wsResult += wsMonth;
4122    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '2')) {
4123      CFX_WideString wsMonth;
4124      wsMonth.Format(L"%02d", month);
4125      wsResult += wsMonth;
4126    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '3')) {
4127      CFX_WideString wsTemp;
4128      pLocale->GetMonthName(month - 1, wsTemp, TRUE);
4129      wsResult += wsTemp;
4130    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '4')) {
4131      CFX_WideString wsTemp;
4132      pLocale->GetMonthName(month - 1, wsTemp, FALSE);
4133      wsResult += wsTemp;
4134    } else if (dwSymbol == FXBSTR_ID(0, 0, 'E', '1')) {
4135      FX_WORD wWeekDay = FX_GetWeekDay(year, month, day);
4136      CFX_WideString wsWeekDay;
4137      wsWeekDay.Format(L"%d", wWeekDay + 1);
4138      wsResult += wsWeekDay;
4139    } else if (dwSymbol == FXBSTR_ID(0, 0, 'E', '3')) {
4140      FX_WORD wWeekDay = FX_GetWeekDay(year, month, day);
4141      CFX_WideString wsTemp;
4142      pLocale->GetDayName(wWeekDay, wsTemp, TRUE);
4143      wsResult += wsTemp;
4144    } else if (dwSymbol == FXBSTR_ID(0, 0, 'E', '4')) {
4145      FX_WORD wWeekDay = FX_GetWeekDay(year, month, day);
4146      if (pLocale) {
4147        CFX_WideString wsTemp;
4148        pLocale->GetDayName(wWeekDay, wsTemp, FALSE);
4149        wsResult += wsTemp;
4150      }
4151    } else if (dwSymbol == FXBSTR_ID(0, 0, 'e', '1')) {
4152      FX_WORD wWeekDay = FX_GetWeekDay(year, month, day);
4153      CFX_WideString wsWeekDay;
4154      wsWeekDay.Format(L"%d", wWeekDay ? wWeekDay : 7);
4155      wsResult += wsWeekDay;
4156    } else if (dwSymbol == FXBSTR_ID(0, 0, 'G', '1')) {
4157      CFX_WideString wsTemp;
4158      pLocale->GetEraName(wsTemp, year < 0);
4159      wsResult += wsTemp;
4160    } else if (dwSymbol == FXBSTR_ID(0, 0, 'Y', '2')) {
4161      CFX_WideString wsYear;
4162      wsYear.Format(L"%02d", year % 100);
4163      wsResult += wsYear;
4164    } else if (dwSymbol == FXBSTR_ID(0, 0, 'Y', '4')) {
4165      CFX_WideString wsYear;
4166      wsYear.Format(L"%d", year);
4167      wsResult += wsYear;
4168    } else if (dwSymbol == FXBSTR_ID(0, 0, 'w', '1')) {
4169      FX_WORD week_index = FX_GetWeekOfMonth(year, month, day);
4170      CFX_WideString wsWeekInMonth;
4171      wsWeekInMonth.Format(L"%d", week_index);
4172      wsResult += wsWeekInMonth;
4173    } else if (dwSymbol == FXBSTR_ID(0, 0, 'W', '2')) {
4174      FX_WORD week_index = FX_GetWeekOfYear(year, month, day);
4175      CFX_WideString wsWeekInYear;
4176      wsWeekInYear.Format(L"%02d", week_index);
4177      wsResult += wsWeekInYear;
4178    }
4179  }
4180  return bRet;
4181}
4182static FX_BOOL FX_TimeFormat(const CFX_WideString& wsTimePattern,
4183                             IFX_Locale* pLocale,
4184                             const CFX_Unitime& datetime,
4185                             CFX_WideString& wsResult) {
4186  FX_BOOL bGMT = FALSE;
4187  FX_BOOL bRet = TRUE;
4188  uint8_t hour = datetime.GetHour();
4189  uint8_t minute = datetime.GetMinute();
4190  uint8_t second = datetime.GetSecond();
4191  FX_WORD millisecond = datetime.GetMillisecond();
4192  int32_t ccf = 0;
4193  const FX_WCHAR* strf = (const FX_WCHAR*)wsTimePattern;
4194  int32_t lenf = wsTimePattern.GetLength();
4195  FX_WORD wHour = hour;
4196  FX_BOOL bPM = FALSE;
4197  if (wsTimePattern.Find('A') != -1) {
4198    if (wHour >= 12) {
4199      bPM = TRUE;
4200    }
4201  }
4202  while (ccf < lenf) {
4203    if (strf[ccf] == '\'') {
4204      wsResult += FX_GetLiteralText(strf, ccf, lenf);
4205      ccf++;
4206      continue;
4207    } else if (FX_Local_Find(gs_wsTimeSymbols, strf[ccf]) < 0) {
4208      wsResult += strf[ccf++];
4209      continue;
4210    }
4211    FX_DWORD dwSymbolNum = 1;
4212    FX_DWORD dwSymbol = strf[ccf++];
4213    while (ccf < lenf && strf[ccf] == dwSymbol) {
4214      ccf++;
4215      dwSymbolNum++;
4216    }
4217    dwSymbol = (dwSymbol << 8) | (dwSymbolNum + '0');
4218    if (dwSymbol == FXBSTR_ID(0, 0, 'h', '1')) {
4219      if (wHour > 12) {
4220        wHour -= 12;
4221      }
4222      CFX_WideString wsHour;
4223      wsHour.Format(L"%d", wHour == 0 ? 12 : wHour);
4224      wsResult += wsHour;
4225    } else if (dwSymbol == FXBSTR_ID(0, 0, 'h', '2')) {
4226      if (wHour > 12) {
4227        wHour -= 12;
4228      }
4229      CFX_WideString wsHour;
4230      wsHour.Format(L"%02d", wHour == 0 ? 12 : wHour);
4231      wsResult += wsHour;
4232    } else if (dwSymbol == FXBSTR_ID(0, 0, 'K', '1')) {
4233      CFX_WideString wsHour;
4234      wsHour.Format(L"%d", wHour == 0 ? 24 : wHour);
4235      wsResult += wsHour;
4236    } else if (dwSymbol == FXBSTR_ID(0, 0, 'K', '2')) {
4237      CFX_WideString wsHour;
4238      wsHour.Format(L"%02d", wHour == 0 ? 24 : wHour);
4239      wsResult += wsHour;
4240    } else if (dwSymbol == FXBSTR_ID(0, 0, 'k', '1')) {
4241      if (wHour > 12) {
4242        wHour -= 12;
4243      }
4244      CFX_WideString wsHour;
4245      wsHour.Format(L"%d", wHour);
4246      wsResult += wsHour;
4247    } else if (dwSymbol == FXBSTR_ID(0, 0, 'H', '1')) {
4248      CFX_WideString wsHour;
4249      wsHour.Format(L"%d", wHour);
4250      wsResult += wsHour;
4251    } else if (dwSymbol == FXBSTR_ID(0, 0, 'k', '2')) {
4252      if (wHour > 12) {
4253        wHour -= 12;
4254      }
4255      CFX_WideString wsHour;
4256      wsHour.Format(L"%02d", wHour);
4257      wsResult += wsHour;
4258    } else if (dwSymbol == FXBSTR_ID(0, 0, 'H', '2')) {
4259      CFX_WideString wsHour;
4260      wsHour.Format(L"%02d", wHour);
4261      wsResult += wsHour;
4262    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '1')) {
4263      CFX_WideString wsMinute;
4264      wsMinute.Format(L"%d", minute);
4265      wsResult += wsMinute;
4266    } else if (dwSymbol == FXBSTR_ID(0, 0, 'M', '2')) {
4267      CFX_WideString wsMinute;
4268      wsMinute.Format(L"%02d", minute);
4269      wsResult += wsMinute;
4270    } else if (dwSymbol == FXBSTR_ID(0, 0, 'S', '1')) {
4271      CFX_WideString wsSecond;
4272      wsSecond.Format(L"%d", second);
4273      wsResult += wsSecond;
4274    } else if (dwSymbol == FXBSTR_ID(0, 0, 'S', '2')) {
4275      CFX_WideString wsSecond;
4276      wsSecond.Format(L"%02d", second);
4277      wsResult += wsSecond;
4278    } else if (dwSymbol == FXBSTR_ID(0, 0, 'F', '3')) {
4279      CFX_WideString wsMilliseconds;
4280      wsMilliseconds.Format(L"%03d", millisecond);
4281      wsResult += wsMilliseconds;
4282    } else if (dwSymbol == FXBSTR_ID(0, 0, 'A', '1')) {
4283      CFX_WideString wsMeridiem;
4284      pLocale->GetMeridiemName(wsMeridiem, !bPM);
4285      wsResult += wsMeridiem;
4286    } else if (dwSymbol == FXBSTR_ID(0, 0, 'Z', '1')) {
4287      wsResult += FX_WSTRC(L"GMT");
4288      FX_TIMEZONE tz;
4289      pLocale->GetTimeZone(tz);
4290      if (!bGMT && (tz.tzHour != 0 || tz.tzMinute != 0)) {
4291        if (tz.tzHour < 0) {
4292          wsResult += FX_WSTRC(L"-");
4293        } else {
4294          wsResult += FX_WSTRC(L"+");
4295        }
4296        CFX_WideString wsTimezone;
4297        wsTimezone.Format(L"%02d:%02d", FXSYS_abs(tz.tzHour), tz.tzMinute);
4298        wsResult += wsTimezone;
4299      }
4300    } else if (dwSymbol == FXBSTR_ID(0, 0, 'z', '1')) {
4301      FX_TIMEZONE tz;
4302      pLocale->GetTimeZone(tz);
4303      if (!bGMT && tz.tzHour != 0 && tz.tzMinute != 0) {
4304        if (tz.tzHour < 0) {
4305          wsResult += FX_WSTRC(L"-");
4306        } else {
4307          wsResult += FX_WSTRC(L"+");
4308        }
4309        CFX_WideString wsTimezone;
4310        wsTimezone.Format(L"%02d:%02d", FXSYS_abs(tz.tzHour), tz.tzMinute);
4311        wsResult += wsTimezone;
4312      }
4313    }
4314  }
4315  return bRet;
4316}
4317static FX_BOOL FX_FormatDateTime(const CFX_Unitime& dt,
4318                                 const CFX_WideString& wsDatePattern,
4319                                 const CFX_WideString& wsTimePattern,
4320                                 FX_BOOL bDateFirst,
4321                                 IFX_Locale* pLocale,
4322                                 CFX_WideString& wsOutput) {
4323  FX_BOOL bRet = TRUE;
4324  CFX_WideString wsDateOut, wsTimeOut;
4325  if (!wsDatePattern.IsEmpty()) {
4326    bRet &= FX_DateFormat(wsDatePattern, pLocale, dt, wsDateOut);
4327  }
4328  if (!wsTimePattern.IsEmpty()) {
4329    bRet &= FX_TimeFormat(wsTimePattern, pLocale, dt, wsTimeOut);
4330  }
4331  wsOutput = bDateFirst ? wsDateOut + wsTimeOut : wsTimeOut + wsDateOut;
4332  return bRet;
4333}
4334FX_BOOL CFX_FormatString::FormatDateTime(const CFX_WideString& wsSrcDateTime,
4335                                         const CFX_WideString& wsPattern,
4336                                         CFX_WideString& wsOutput) {
4337  if (wsSrcDateTime.IsEmpty() || wsPattern.IsEmpty()) {
4338    return FALSE;
4339  }
4340  CFX_WideString wsDatePattern, wsTimePattern;
4341  IFX_Locale* pLocale = NULL;
4342  FX_DATETIMETYPE eCategory =
4343      GetDateTimeFormat(wsPattern, pLocale, wsDatePattern, wsTimePattern);
4344  if (pLocale == NULL || eCategory == FX_DATETIMETYPE_Unknown) {
4345    return FALSE;
4346  }
4347  CFX_Unitime dt(0);
4348  int32_t iT = wsSrcDateTime.Find(L"T");
4349  if (iT < 0) {
4350    if (eCategory == FX_DATETIMETYPE_Date) {
4351      FX_DateFromCanonical(wsSrcDateTime, dt);
4352    } else if (eCategory == FX_DATETIMETYPE_Time) {
4353      FX_TimeFromCanonical(wsSrcDateTime, dt, pLocale);
4354    }
4355  } else {
4356    FX_DateFromCanonical(wsSrcDateTime.Left(iT), dt);
4357    FX_TimeFromCanonical(
4358        wsSrcDateTime.Right(wsSrcDateTime.GetLength() - iT - 1), dt, pLocale);
4359  }
4360  return FX_FormatDateTime(dt, wsDatePattern, wsTimePattern,
4361                           eCategory != FX_DATETIMETYPE_TimeDate, pLocale,
4362                           wsOutput);
4363}
4364FX_BOOL CFX_FormatString::FormatDateTime(const CFX_WideString& wsSrcDateTime,
4365                                         const CFX_WideString& wsPattern,
4366                                         CFX_WideString& wsOutput,
4367                                         FX_DATETIMETYPE eDateTimeType) {
4368  if (wsSrcDateTime.IsEmpty() || wsPattern.IsEmpty()) {
4369    return FALSE;
4370  }
4371  CFX_WideString wsDatePattern, wsTimePattern;
4372  IFX_Locale* pLocale = NULL;
4373  FX_DATETIMETYPE eCategory =
4374      GetDateTimeFormat(wsPattern, pLocale, wsDatePattern, wsTimePattern);
4375  if (!pLocale) {
4376    return FALSE;
4377  }
4378  if (eCategory == FX_DATETIMETYPE_Unknown) {
4379    if (eDateTimeType == FX_DATETIMETYPE_Time) {
4380      wsTimePattern = wsDatePattern;
4381      wsDatePattern.Empty();
4382    }
4383    eCategory = eDateTimeType;
4384  }
4385  if (eCategory == FX_DATETIMETYPE_Unknown) {
4386    return FALSE;
4387  }
4388  CFX_Unitime dt(0);
4389  int32_t iT = wsSrcDateTime.Find(L"T");
4390  if (iT < 0) {
4391    if (eCategory == FX_DATETIMETYPE_Date &&
4392        FX_DateFromCanonical(wsSrcDateTime, dt)) {
4393      return FX_FormatDateTime(dt, wsDatePattern, wsTimePattern, TRUE, pLocale,
4394                               wsOutput);
4395    } else if (eCategory == FX_DATETIMETYPE_Time &&
4396               FX_TimeFromCanonical(wsSrcDateTime, dt, pLocale)) {
4397      return FX_FormatDateTime(dt, wsDatePattern, wsTimePattern, TRUE, pLocale,
4398                               wsOutput);
4399    }
4400  } else {
4401    CFX_WideStringC wsSrcDate((const FX_WCHAR*)wsSrcDateTime, iT);
4402    CFX_WideStringC wsSrcTime((const FX_WCHAR*)wsSrcDateTime + iT + 1,
4403                              wsSrcDateTime.GetLength() - iT - 1);
4404    if (wsSrcDate.IsEmpty() || wsSrcTime.IsEmpty()) {
4405      return FALSE;
4406    }
4407    if (FX_DateFromCanonical(wsSrcDate, dt) &&
4408        FX_TimeFromCanonical(wsSrcTime, dt, pLocale)) {
4409      return FX_FormatDateTime(dt, wsDatePattern, wsTimePattern,
4410                               eCategory != FX_DATETIMETYPE_TimeDate, pLocale,
4411                               wsOutput);
4412    }
4413  }
4414  return FALSE;
4415}
4416FX_BOOL CFX_FormatString::FormatDateTime(const CFX_Unitime& dt,
4417                                         const CFX_WideString& wsPattern,
4418                                         CFX_WideString& wsOutput) {
4419  if (wsPattern.IsEmpty()) {
4420    return FALSE;
4421  }
4422  CFX_WideString wsDatePattern, wsTimePattern;
4423  IFX_Locale* pLocale = NULL;
4424  FX_DATETIMETYPE eCategory =
4425      GetDateTimeFormat(wsPattern, pLocale, wsDatePattern, wsTimePattern);
4426  if (!pLocale) {
4427    return FALSE;
4428  }
4429  return FX_FormatDateTime(dt, wsPattern, wsTimePattern,
4430                           eCategory != FX_DATETIMETYPE_TimeDate, pLocale,
4431                           wsOutput);
4432}
4433FX_BOOL CFX_FormatString::FormatZero(const CFX_WideString& wsPattern,
4434                                     CFX_WideString& wsOutput) {
4435  if (wsPattern.IsEmpty()) {
4436    return FALSE;
4437  }
4438  CFX_WideString wsTextFormat;
4439  GetTextFormat(wsPattern, FX_WSTRC(L"zero"), wsTextFormat);
4440  int32_t iPattern = 0;
4441  const FX_WCHAR* pStrPattern = (const FX_WCHAR*)wsTextFormat;
4442  int32_t iLenPattern = wsTextFormat.GetLength();
4443  while (iPattern < iLenPattern) {
4444    if (pStrPattern[iPattern] == '\'') {
4445      wsOutput += FX_GetLiteralText(pStrPattern, iPattern, iLenPattern);
4446      iPattern++;
4447      continue;
4448    } else {
4449      wsOutput += pStrPattern[iPattern++];
4450      continue;
4451    }
4452  }
4453  return TRUE;
4454}
4455FX_BOOL CFX_FormatString::FormatNull(const CFX_WideString& wsPattern,
4456                                     CFX_WideString& wsOutput) {
4457  if (wsPattern.IsEmpty()) {
4458    return FALSE;
4459  }
4460  CFX_WideString wsTextFormat;
4461  GetTextFormat(wsPattern, FX_WSTRC(L"null"), wsTextFormat);
4462  int32_t iPattern = 0;
4463  const FX_WCHAR* pStrPattern = (const FX_WCHAR*)wsTextFormat;
4464  int32_t iLenPattern = wsTextFormat.GetLength();
4465  while (iPattern < iLenPattern) {
4466    if (pStrPattern[iPattern] == '\'') {
4467      wsOutput += FX_GetLiteralText(pStrPattern, iPattern, iLenPattern);
4468      iPattern++;
4469      continue;
4470    } else {
4471      wsOutput += pStrPattern[iPattern++];
4472      continue;
4473    }
4474  }
4475  return TRUE;
4476}
4477IFX_Locale* CFX_FormatString::GetPatternLocale(
4478    const CFX_WideStringC& wsLocale) {
4479  if (m_bUseLCID) {
4480  }
4481  return m_pLocaleMgr->GetLocaleByName(wsLocale);
4482}
4483#define FXMATH_DECIMAL_SCALELIMIT 0x1c
4484#define FXMATH_DECIMAL_NEGMASK (0x80000000L)
4485#define FXMATH_DECIMAL_FORCEBOOL(x) (!(!(x)))
4486#define FXMATH_DECIMAL_MAKEFLAGS(NEG, SCALE) \
4487  (((SCALE) << 0x10) | ((NEG) ? FXMATH_DECIMAL_NEGMASK : 0))
4488#define FXMATH_DECIMAL_FLAGS2NEG(FLAGS) \
4489  FXMATH_DECIMAL_FORCEBOOL((FLAGS)&FXMATH_DECIMAL_NEGMASK)
4490#define FXMATH_DECIMAL_FLAGS2SCALE(FLAGS) \
4491  ((uint8_t)(((FLAGS) & ~FXMATH_DECIMAL_NEGMASK) >> 0x10))
4492#define FXMATH_DECIMAL_RSHIFT32BIT(x) ((x) >> 0x10 >> 0x10)
4493#define FXMATH_DECIMAL_LSHIFT32BIT(x) ((x) << 0x10 << 0x10)
4494static inline uint8_t fxmath_decimal_helper_div10(uint64_t& phi,
4495                                                  uint64_t& pmid,
4496                                                  uint64_t& plo) {
4497  uint8_t retVal;
4498  pmid += FXMATH_DECIMAL_LSHIFT32BIT(phi % 0xA);
4499  phi /= 0xA;
4500  plo += FXMATH_DECIMAL_LSHIFT32BIT(pmid % 0xA);
4501  pmid /= 0xA;
4502  retVal = plo % 0xA;
4503  plo /= 0xA;
4504  return retVal;
4505}
4506static inline uint8_t fxmath_decimal_helper_div10_any(uint64_t nums[],
4507                                                      uint8_t numcount) {
4508  uint8_t retVal = 0;
4509  for (int i = numcount - 1; i > 0; i--) {
4510    nums[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(nums[i] % 0xA);
4511    nums[i] /= 0xA;
4512  }
4513  if (numcount) {
4514    retVal = nums[0] % 0xA;
4515    nums[0] /= 0xA;
4516  }
4517  return retVal;
4518}
4519static inline void fxmath_decimal_helper_mul10(uint64_t& phi,
4520                                               uint64_t& pmid,
4521                                               uint64_t& plo) {
4522  plo *= 0xA;
4523  pmid = pmid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(plo);
4524  plo = (uint32_t)plo;
4525  phi = phi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(pmid);
4526  pmid = (uint32_t)pmid;
4527}
4528static inline void fxmath_decimal_helper_mul10_any(uint64_t nums[],
4529                                                   uint8_t numcount) {
4530  nums[0] *= 0xA;
4531  for (int i = 1; i < numcount; i++) {
4532    nums[i] = nums[i] * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(nums[i - 1]);
4533    nums[i - 1] = (uint32_t)nums[i - 1];
4534  }
4535}
4536static inline void fxmath_decimal_helper_normalize(uint64_t& phi,
4537                                                   uint64_t& pmid,
4538                                                   uint64_t& plo) {
4539  phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
4540  pmid = (uint32_t)pmid;
4541  pmid += FXMATH_DECIMAL_RSHIFT32BIT(plo);
4542  plo = (uint32_t)plo;
4543  phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
4544  pmid = (uint32_t)pmid;
4545}
4546static inline void fxmath_decimal_helper_normalize_any(uint64_t nums[],
4547                                                       uint8_t len) {
4548  {
4549    for (int i = len - 2; i > 0; i--) {
4550      nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
4551      nums[i] = (uint32_t)nums[i];
4552    }
4553  }
4554  {
4555    for (int i = 0; i < len - 1; i++) {
4556      nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
4557      nums[i] = (uint32_t)nums[i];
4558    }
4559  }
4560}
4561static inline int8_t fxmath_decimal_helper_raw_compare(uint32_t hi1,
4562                                                       uint32_t mid1,
4563                                                       uint32_t lo1,
4564                                                       uint32_t hi2,
4565                                                       uint32_t mid2,
4566                                                       uint32_t lo2) {
4567  int8_t retVal = 0;
4568  if (!retVal) {
4569    retVal += (hi1 > hi2 ? 1 : (hi1 < hi2 ? -1 : 0));
4570  }
4571  if (!retVal) {
4572    retVal += (mid1 > mid2 ? 1 : (mid1 < mid2 ? -1 : 0));
4573  }
4574  if (!retVal) {
4575    retVal += (lo1 > lo2 ? 1 : (lo1 < lo2 ? -1 : 0));
4576  }
4577  return retVal;
4578}
4579static inline int8_t fxmath_decimal_helper_raw_compare_any(uint64_t a[],
4580                                                           uint8_t al,
4581                                                           uint64_t b[],
4582                                                           uint8_t bl) {
4583  int8_t retVal = 0;
4584  for (int i = std::max(al - 1, bl - 1); i >= 0; i--) {
4585    uint64_t l = (i >= al ? 0 : a[i]), r = (i >= bl ? 0 : b[i]);
4586    retVal += (l > r ? 1 : (l < r ? -1 : 0));
4587    if (retVal) {
4588      return retVal;
4589    }
4590  }
4591  return retVal;
4592}
4593static inline void fxmath_decimal_helper_dec_any(uint64_t a[], uint8_t al) {
4594  for (int i = 0; i < al; i++) {
4595    if (a[i]--) {
4596      return;
4597    }
4598  }
4599}
4600static inline void fxmath_decimal_helper_inc_any(uint64_t a[], uint8_t al) {
4601  for (int i = 0; i < al; i++) {
4602    a[i]++;
4603    if ((uint32_t)a[i] == a[i]) {
4604      return;
4605    }
4606    a[i] = 0;
4607  }
4608}
4609static inline void fxmath_decimal_helper_raw_mul(uint64_t a[],
4610                                                 uint8_t al,
4611                                                 uint64_t b[],
4612                                                 uint8_t bl,
4613                                                 uint64_t c[],
4614                                                 uint8_t cl) {
4615  assert(al + bl <= cl);
4616  {
4617    for (int i = 0; i < cl; i++) {
4618      c[i] = 0;
4619    }
4620  }
4621  {
4622    for (int i = 0; i < al; i++) {
4623      for (int j = 0; j < bl; j++) {
4624        uint64_t m = (uint64_t)a[i] * b[j];
4625        c[i + j] += (uint32_t)m;
4626        c[i + j + 1] += FXMATH_DECIMAL_RSHIFT32BIT(m);
4627      }
4628    }
4629  }
4630  {
4631    for (int i = 0; i < cl - 1; i++) {
4632      c[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(c[i]);
4633      c[i] = (uint32_t)c[i];
4634    }
4635  }
4636  {
4637    for (int i = 0; i < cl; i++) {
4638      c[i] = (uint32_t)c[i];
4639    }
4640  }
4641}
4642static inline void fxmath_decimal_helper_raw_div(uint64_t a[],
4643                                                 uint8_t al,
4644                                                 uint64_t b[],
4645                                                 uint8_t bl,
4646                                                 uint64_t c[],
4647                                                 uint8_t cl) {
4648  int i;
4649  for (i = 0; i < cl; i++) {
4650    c[i] = 0;
4651  }
4652  uint64_t left[16] = {0}, right[16] = {0};
4653  left[0] = 0;
4654  for (i = 0; i < al; i++) {
4655    right[i] = a[i];
4656  }
4657  uint64_t tmp[16];
4658  while (fxmath_decimal_helper_raw_compare_any(left, al, right, al) <= 0) {
4659    uint64_t cur[16];
4660    for (i = 0; i < al; i++) {
4661      cur[i] = left[i] + right[i];
4662    }
4663    for (i = al - 1; i >= 0; i--) {
4664      if (i) {
4665        cur[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(cur[i] % 2);
4666      }
4667      cur[i] /= 2;
4668    }
4669    fxmath_decimal_helper_raw_mul(cur, al, b, bl, tmp, 16);
4670    switch (fxmath_decimal_helper_raw_compare_any(tmp, 16, a, al)) {
4671      case -1:
4672        for (i = 0; i < 16; i++) {
4673          left[i] = cur[i];
4674        }
4675        left[0]++;
4676        fxmath_decimal_helper_normalize_any(left, al);
4677        break;
4678      case 1:
4679        for (i = 0; i < 16; i++) {
4680          right[i] = cur[i];
4681        }
4682        fxmath_decimal_helper_dec_any(right, al);
4683        break;
4684      case 0:
4685        for (i = 0; i < std::min(al, cl); i++) {
4686          c[i] = cur[i];
4687        }
4688        return;
4689    }
4690  }
4691  for (i = 0; i < std::min(al, cl); i++) {
4692    c[i] = left[i];
4693  }
4694}
4695static inline FX_BOOL fxmath_decimal_helper_outofrange(uint64_t a[],
4696                                                       uint8_t al,
4697                                                       uint8_t goal) {
4698  for (int i = goal; i < al; i++) {
4699    if (a[i]) {
4700      return TRUE;
4701    }
4702  }
4703  return FALSE;
4704}
4705static inline void fxmath_decimal_helper_shrinkintorange(uint64_t a[],
4706                                                         uint8_t al,
4707                                                         uint8_t goal,
4708                                                         uint8_t& scale) {
4709  FX_BOOL bRoundUp = FALSE;
4710  while (scale != 0 && (scale > FXMATH_DECIMAL_SCALELIMIT ||
4711                        fxmath_decimal_helper_outofrange(a, al, goal))) {
4712    bRoundUp = fxmath_decimal_helper_div10_any(a, al) >= 5;
4713    scale--;
4714  }
4715  if (bRoundUp) {
4716    fxmath_decimal_helper_normalize_any(a, goal);
4717    fxmath_decimal_helper_inc_any(a, goal);
4718  }
4719}
4720static inline void fxmath_decimal_helper_truncate(uint64_t& phi,
4721                                                  uint64_t& pmid,
4722                                                  uint64_t& plo,
4723                                                  uint8_t& scale,
4724                                                  uint8_t minscale = 0) {
4725  while (scale > minscale) {
4726    uint64_t thi = phi, tmid = pmid, tlo = plo;
4727    if (fxmath_decimal_helper_div10(thi, tmid, tlo) != 0) {
4728      break;
4729    }
4730    phi = thi, pmid = tmid, plo = tlo;
4731    scale--;
4732  }
4733}
4734CFX_Decimal::CFX_Decimal() {
4735  m_uLo = m_uMid = m_uHi = m_uFlags = 0;
4736}
4737CFX_Decimal::CFX_Decimal(uint64_t val) {
4738  m_uLo = (uint32_t)val;
4739  m_uMid = (uint32_t)FXMATH_DECIMAL_RSHIFT32BIT(val);
4740  m_uHi = 0;
4741  m_uFlags = 0;
4742}
4743CFX_Decimal::CFX_Decimal(uint32_t val) {
4744  m_uLo = (uint32_t)val;
4745  m_uMid = m_uHi = 0;
4746  m_uFlags = 0;
4747}
4748CFX_Decimal::CFX_Decimal(uint32_t lo,
4749                         uint32_t mid,
4750                         uint32_t hi,
4751                         FX_BOOL neg,
4752                         uint8_t scale) {
4753  scale = (scale > FXMATH_DECIMAL_SCALELIMIT ? 0 : scale);
4754  m_uLo = lo;
4755  m_uMid = mid;
4756  m_uHi = hi;
4757  m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(neg && IsNotZero(), scale);
4758}
4759CFX_Decimal::CFX_Decimal(int32_t val) {
4760  if (val >= 0) {
4761    *this = CFX_Decimal((uint32_t)val);
4762  } else {
4763    *this = CFX_Decimal((uint32_t)-val);
4764    SetNegate();
4765  }
4766}
4767CFX_Decimal::CFX_Decimal(int64_t val) {
4768  if (val >= 0) {
4769    *this = CFX_Decimal((uint64_t)val);
4770  } else {
4771    *this = CFX_Decimal((uint64_t)-val);
4772    SetNegate();
4773  }
4774}
4775CFX_Decimal::CFX_Decimal(FX_FLOAT val, uint8_t scale) {
4776  FX_FLOAT newval = fabs(val);
4777  uint64_t phi, pmid, plo;
4778  plo = (uint64_t)newval;
4779  pmid = (uint64_t)(newval / 1e32);
4780  phi = (uint64_t)(newval / 1e64);
4781  newval = FXSYS_fmod(newval, 1.0f);
4782  for (uint8_t iter = 0; iter < scale; iter++) {
4783    fxmath_decimal_helper_mul10(phi, pmid, plo);
4784    newval *= 10;
4785    plo += (uint64_t)newval;
4786    newval = FXSYS_fmod(newval, 1.0f);
4787  }
4788  plo += FXSYS_round(newval);
4789  fxmath_decimal_helper_normalize(phi, pmid, plo);
4790  m_uHi = (uint32_t)phi;
4791  m_uMid = (uint32_t)pmid;
4792  m_uLo = (uint32_t)plo;
4793  m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(val < 0 && IsNotZero(), scale);
4794}
4795CFX_Decimal::CFX_Decimal(const CFX_WideStringC& strObj) {
4796  const FX_WCHAR* str = strObj.GetPtr();
4797  const FX_WCHAR* strBound = str + strObj.GetLength();
4798  FX_BOOL pointmet = 0;
4799  FX_BOOL negmet = 0;
4800  uint8_t scale = 0;
4801  m_uHi = m_uMid = m_uLo = 0;
4802  while (str != strBound && *str == ' ') {
4803    str++;
4804  }
4805  if (str != strBound && *str == '-') {
4806    negmet = 1;
4807    str++;
4808  } else if (str != strBound && *str == '+') {
4809    str++;
4810  }
4811  while (str != strBound && ((*str >= '0' && *str <= '9') || *str == '.') &&
4812         scale < FXMATH_DECIMAL_SCALELIMIT) {
4813    if (*str == '.') {
4814      if (pointmet) {
4815        goto cont;
4816      }
4817      pointmet = 1;
4818    } else {
4819      m_uHi = m_uHi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uMid * 0xA);
4820      m_uMid = m_uMid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uLo * 0xA);
4821      m_uLo = m_uLo * 0xA + (*str - '0');
4822      if (pointmet) {
4823        scale++;
4824      }
4825    }
4826  cont:
4827    str++;
4828  }
4829  m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(negmet && IsNotZero(), scale);
4830}
4831CFX_Decimal::CFX_Decimal(const CFX_ByteStringC& strObj) {
4832  CFX_WideString wstrObj;
4833  wstrObj.ConvertFrom(strObj);
4834  *this = CFX_Decimal(wstrObj);
4835}
4836CFX_Decimal::operator CFX_WideString() const {
4837  CFX_WideString retString;
4838  CFX_WideString tmpbuf;
4839  uint64_t phi = m_uHi, pmid = m_uMid, plo = m_uLo;
4840  while (phi || pmid || plo) {
4841    tmpbuf += fxmath_decimal_helper_div10(phi, pmid, plo) + '0';
4842  }
4843  uint8_t outputlen = (uint8_t)tmpbuf.GetLength();
4844  uint8_t scale = (uint8_t)FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
4845  while (scale >= outputlen) {
4846    tmpbuf += '0';
4847    outputlen++;
4848  }
4849  if (FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) && IsNotZero()) {
4850    retString += '-';
4851  }
4852  for (uint8_t idx = 0; idx < outputlen; idx++) {
4853    if (idx == (outputlen - scale) && scale != 0) {
4854      retString += '.';
4855    }
4856    retString += tmpbuf[outputlen - 1 - idx];
4857  }
4858  return retString;
4859}
4860CFX_Decimal::operator double() const {
4861  double pow = (double)(1 << 16) * (1 << 16);
4862  double base =
4863      ((double)m_uHi) * pow * pow + ((double)m_uMid) * pow + ((double)m_uLo);
4864  int8_t scale = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
4865  FX_BOOL bNeg = FXMATH_DECIMAL_FLAGS2NEG(m_uFlags);
4866  return (bNeg ? -1 : 1) * base * ::pow(10.0, -scale);
4867}
4868void CFX_Decimal::SetScale(uint8_t newscale) {
4869  uint8_t oldscale = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
4870  if (newscale > oldscale) {
4871    uint64_t phi = m_uHi, pmid = m_uMid, plo = m_uLo;
4872    for (uint8_t iter = 0; iter < newscale - oldscale; iter++) {
4873      fxmath_decimal_helper_mul10(phi, pmid, plo);
4874    }
4875    m_uHi = (uint32_t)phi;
4876    m_uMid = (uint32_t)pmid;
4877    m_uLo = (uint32_t)plo;
4878    m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(
4879        FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) && IsNotZero(), newscale);
4880  } else if (newscale < oldscale) {
4881    uint64_t phi, pmid, plo;
4882    phi = 0, pmid = 0, plo = 5;
4883    {
4884      for (uint8_t iter = 0; iter < oldscale - newscale - 1; iter++) {
4885        fxmath_decimal_helper_mul10(phi, pmid, plo);
4886      }
4887    }
4888    phi += m_uHi;
4889    pmid += m_uMid;
4890    plo += m_uLo;
4891    fxmath_decimal_helper_normalize(phi, pmid, plo);
4892    {
4893      for (uint8_t iter = 0; iter < oldscale - newscale; iter++) {
4894        fxmath_decimal_helper_div10(phi, pmid, plo);
4895      }
4896    }
4897    m_uHi = (uint32_t)phi;
4898    m_uMid = (uint32_t)pmid;
4899    m_uLo = (uint32_t)plo;
4900    m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(
4901        FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) && IsNotZero(), newscale);
4902  }
4903}
4904uint8_t CFX_Decimal::GetScale() {
4905  uint8_t oldscale = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
4906  return oldscale;
4907}
4908void CFX_Decimal::SetAbs() {
4909  m_uFlags &= ~FXMATH_DECIMAL_NEGMASK;
4910}
4911void CFX_Decimal::SetNegate() {
4912  if (IsNotZero()) {
4913    m_uFlags ^= FXMATH_DECIMAL_NEGMASK;
4914  }
4915}
4916void CFX_Decimal::FloorOrCeil(FX_BOOL bFloor) {
4917  uint64_t nums[3] = {m_uLo, m_uMid, m_uHi};
4918  FX_BOOL bDataLoss = FALSE;
4919  for (int i = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags); i > 0; i--) {
4920    bDataLoss = fxmath_decimal_helper_div10_any(nums, 3) || bDataLoss;
4921  }
4922  if (bDataLoss && (bFloor ? FXMATH_DECIMAL_FLAGS2NEG(m_uFlags)
4923                           : !FXMATH_DECIMAL_FLAGS2NEG(m_uFlags))) {
4924    fxmath_decimal_helper_inc_any(nums, 3);
4925  }
4926  m_uHi = (uint32_t)nums[2];
4927  m_uMid = (uint32_t)nums[1];
4928  m_uLo = (uint32_t)nums[0];
4929  m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(
4930      FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) && IsNotZero(), 0);
4931}
4932void CFX_Decimal::SetFloor() {
4933  FloorOrCeil(TRUE);
4934}
4935void CFX_Decimal::SetCeiling() {
4936  FloorOrCeil(FALSE);
4937}
4938void CFX_Decimal::SetTruncate() {
4939  FloorOrCeil(!FXMATH_DECIMAL_FLAGS2NEG(m_uFlags));
4940}
4941void CFX_Decimal::Swap(CFX_Decimal& val) {
4942  uint32_t tmp;
4943  tmp = m_uHi;
4944  m_uHi = val.m_uHi;
4945  val.m_uHi = tmp;
4946  tmp = m_uMid;
4947  m_uMid = val.m_uMid;
4948  val.m_uMid = tmp;
4949  tmp = m_uLo;
4950  m_uLo = val.m_uLo;
4951  val.m_uLo = tmp;
4952  tmp = m_uFlags;
4953  m_uFlags = val.m_uFlags;
4954  val.m_uFlags = tmp;
4955}
4956int8_t CFX_Decimal::Compare(const CFX_Decimal& val) const {
4957  CFX_Decimal lhs = *this, rhs = val;
4958  int8_t retVal = 0;
4959  if (FXMATH_DECIMAL_FLAGS2SCALE(lhs.m_uFlags) !=
4960      FXMATH_DECIMAL_FLAGS2SCALE(rhs.m_uFlags)) {
4961    uint8_t scale = std::min(FXMATH_DECIMAL_FLAGS2SCALE(lhs.m_uFlags),
4962                             FXMATH_DECIMAL_FLAGS2SCALE(rhs.m_uFlags));
4963    lhs.SetScale(scale);
4964    rhs.SetScale(scale);
4965  }
4966  retVal = -(FXMATH_DECIMAL_FLAGS2NEG(lhs.m_uFlags) -
4967             FXMATH_DECIMAL_FLAGS2NEG(rhs.m_uFlags));
4968  if (retVal) {
4969    return retVal;
4970  }
4971  retVal = fxmath_decimal_helper_raw_compare(lhs.m_uHi, lhs.m_uMid, lhs.m_uLo,
4972                                             rhs.m_uHi, rhs.m_uMid, rhs.m_uLo);
4973  return (FXMATH_DECIMAL_FLAGS2NEG(lhs.m_uFlags) ? -retVal : retVal);
4974}
4975CFX_Decimal CFX_Decimal::AddOrMinus(const CFX_Decimal& val,
4976                                    FX_BOOL isAdding) const {
4977  CFX_Decimal lhs = *this, rhs = val;
4978  if (FXMATH_DECIMAL_FLAGS2SCALE(lhs.m_uFlags) !=
4979      FXMATH_DECIMAL_FLAGS2SCALE(rhs.m_uFlags)) {
4980    uint8_t scale = std::max(FXMATH_DECIMAL_FLAGS2SCALE(lhs.m_uFlags),
4981                             FXMATH_DECIMAL_FLAGS2SCALE(rhs.m_uFlags));
4982    lhs.SetScale(scale);
4983    rhs.SetScale(scale);
4984  }
4985  if (!isAdding) {
4986    rhs.SetNegate();
4987  }
4988  FX_BOOL doRawAdd = (FXMATH_DECIMAL_FLAGS2NEG(lhs.m_uFlags) ==
4989                      FXMATH_DECIMAL_FLAGS2NEG(rhs.m_uFlags));
4990  if (doRawAdd) {
4991    uint64_t phi = lhs.m_uHi, pmid = lhs.m_uMid, plo = lhs.m_uLo;
4992    phi += rhs.m_uHi;
4993    pmid += rhs.m_uMid;
4994    plo += rhs.m_uLo;
4995    fxmath_decimal_helper_normalize(phi, pmid, plo);
4996    if (FXMATH_DECIMAL_RSHIFT32BIT(phi) &&
4997        FXMATH_DECIMAL_FLAGS2SCALE(lhs.m_uFlags) != 0) {
4998      fxmath_decimal_helper_div10(phi, pmid, plo);
4999      lhs.m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(
5000          FXMATH_DECIMAL_FLAGS2NEG(lhs.m_uFlags),
5001          FXMATH_DECIMAL_FLAGS2SCALE(lhs.m_uFlags) - 1);
5002    }
5003    lhs.m_uHi = (uint32_t)phi;
5004    lhs.m_uMid = (uint32_t)pmid;
5005    lhs.m_uLo = (uint32_t)plo;
5006    return lhs;
5007  } else {
5008    if (fxmath_decimal_helper_raw_compare(lhs.m_uHi, lhs.m_uMid, lhs.m_uLo,
5009                                          rhs.m_uHi, rhs.m_uMid,
5010                                          rhs.m_uLo) < 0) {
5011      lhs.Swap(rhs);
5012    }
5013    lhs.m_uHi -= rhs.m_uHi;
5014    if (lhs.m_uMid < rhs.m_uMid) {
5015      lhs.m_uHi--;
5016    }
5017    lhs.m_uMid -= rhs.m_uMid;
5018    if (lhs.m_uLo < rhs.m_uLo) {
5019      if (!lhs.m_uMid) {
5020        lhs.m_uHi--;
5021      }
5022      lhs.m_uMid--;
5023    }
5024    lhs.m_uLo -= rhs.m_uLo;
5025    return lhs;
5026  }
5027}
5028CFX_Decimal CFX_Decimal::Multiply(const CFX_Decimal& val) const {
5029  uint64_t a[3] = {m_uLo, m_uMid, m_uHi},
5030           b[3] = {val.m_uLo, val.m_uMid, val.m_uHi};
5031  uint64_t c[6];
5032  fxmath_decimal_helper_raw_mul(a, 3, b, 3, c, 6);
5033  FX_BOOL neg = FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) ^
5034                FXMATH_DECIMAL_FLAGS2NEG(val.m_uFlags);
5035  uint8_t scale = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags) +
5036                  FXMATH_DECIMAL_FLAGS2SCALE(val.m_uFlags);
5037  fxmath_decimal_helper_shrinkintorange(c, 6, 3, scale);
5038  return CFX_Decimal((uint32_t)c[0], (uint32_t)c[1], (uint32_t)c[2], neg,
5039                     scale);
5040}
5041CFX_Decimal CFX_Decimal::Divide(const CFX_Decimal& val) const {
5042  if (!val.IsNotZero()) {
5043    return CFX_Decimal();
5044  }
5045  FX_BOOL neg = FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) ^
5046                FXMATH_DECIMAL_FLAGS2NEG(val.m_uFlags);
5047  uint64_t a[7] = {m_uLo, m_uMid, m_uHi},
5048           b[3] = {val.m_uLo, val.m_uMid, val.m_uHi}, c[7] = {0};
5049  uint8_t scale = 0;
5050  if (FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags) <
5051      FXMATH_DECIMAL_FLAGS2SCALE(val.m_uFlags)) {
5052    for (int i = FXMATH_DECIMAL_FLAGS2SCALE(val.m_uFlags) -
5053                 FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
5054         i > 0; i--) {
5055      fxmath_decimal_helper_mul10_any(a, 7);
5056    }
5057  } else {
5058    scale = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags) -
5059            FXMATH_DECIMAL_FLAGS2SCALE(val.m_uFlags);
5060  }
5061  uint8_t minscale = scale;
5062  if (!IsNotZero()) {
5063    return CFX_Decimal(0, 0, 0, 0, minscale);
5064  }
5065  while (!a[6]) {
5066    fxmath_decimal_helper_mul10_any(a, 7);
5067    scale++;
5068  }
5069  fxmath_decimal_helper_div10_any(a, 7);
5070  scale--;
5071  fxmath_decimal_helper_raw_div(a, 6, b, 3, c, 7);
5072  fxmath_decimal_helper_shrinkintorange(c, 6, 3, scale);
5073  fxmath_decimal_helper_truncate(c[2], c[1], c[0], scale, minscale);
5074  return CFX_Decimal((uint32_t)c[0], (uint32_t)c[1], (uint32_t)c[2], neg,
5075                     scale);
5076}
5077CFX_Decimal CFX_Decimal::Modulus(const CFX_Decimal& val) const {
5078  CFX_Decimal lhs = *this, rhs_abs = val;
5079  rhs_abs.SetAbs();
5080  if (!rhs_abs.IsNotZero()) {
5081    return *this;
5082  }
5083  while (TRUE) {
5084    CFX_Decimal lhs_abs = lhs;
5085    lhs_abs.SetAbs();
5086    if (lhs_abs < rhs_abs) {
5087      break;
5088    }
5089    CFX_Decimal quot = lhs / rhs_abs;
5090    quot.SetTruncate();
5091    lhs = lhs - quot * rhs_abs;
5092  }
5093  return lhs;
5094}
5095FX_BOOL CFX_Decimal::operator==(const CFX_Decimal& val) const {
5096  return Compare(val) == 0;
5097}
5098FX_BOOL CFX_Decimal::operator<=(const CFX_Decimal& val) const {
5099  return Compare(val) <= 0;
5100}
5101FX_BOOL CFX_Decimal::operator>=(const CFX_Decimal& val) const {
5102  return Compare(val) >= 0;
5103}
5104FX_BOOL CFX_Decimal::operator!=(const CFX_Decimal& val) const {
5105  return Compare(val) != 0;
5106}
5107FX_BOOL CFX_Decimal::operator<(const CFX_Decimal& val) const {
5108  return Compare(val) < 0;
5109}
5110FX_BOOL CFX_Decimal::operator>(const CFX_Decimal& val) const {
5111  return Compare(val) > 0;
5112}
5113CFX_Decimal CFX_Decimal::operator+(const CFX_Decimal& val) const {
5114  return AddOrMinus(val, TRUE);
5115}
5116CFX_Decimal CFX_Decimal::operator-(const CFX_Decimal& val) const {
5117  return AddOrMinus(val, FALSE);
5118}
5119CFX_Decimal CFX_Decimal::operator*(const CFX_Decimal& val) const {
5120  return Multiply(val);
5121}
5122CFX_Decimal CFX_Decimal::operator/(const CFX_Decimal& val) const {
5123  return Divide(val);
5124}
5125CFX_Decimal CFX_Decimal::operator%(const CFX_Decimal& val) const {
5126  return Modulus(val);
5127}
5128