Home | History | Annotate | Download | only in crt
      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 "xfa/fgas/crt/cfgas_formatstring.h"
      8 
      9 #include <algorithm>
     10 #include <vector>
     11 
     12 #include "core/fxcrt/cfx_decimal.h"
     13 #include "core/fxcrt/fx_extension.h"
     14 #include "core/fxcrt/xml/cxml_element.h"
     15 
     16 #define FX_LOCALECATEGORY_DateHash 0xbde9abde
     17 #define FX_LOCALECATEGORY_TimeHash 0x2d71b00f
     18 #define FX_LOCALECATEGORY_DateTimeHash 0x158c72ed
     19 #define FX_LOCALECATEGORY_NumHash 0x0b4ff870
     20 #define FX_LOCALECATEGORY_TextHash 0x2d08af85
     21 #define FX_LOCALECATEGORY_ZeroHash 0x568cb500
     22 #define FX_LOCALECATEGORY_NullHash 0x052931bb
     23 
     24 #define FX_NUMSTYLE_Percent 0x01
     25 #define FX_NUMSTYLE_Exponent 0x02
     26 #define FX_NUMSTYLE_DotVorv 0x04
     27 
     28 namespace {
     29 
     30 struct FX_LOCALESUBCATEGORYINFO {
     31   uint32_t uHash;
     32   const wchar_t* pName;
     33   int32_t eSubCategory;
     34 };
     35 
     36 const FX_LOCALESUBCATEGORYINFO g_FXLocaleDateTimeSubCatData[] = {
     37     {0x14da2125, L"default", FX_LOCALEDATETIMESUBCATEGORY_Default},
     38     {0x9041d4b0, L"short", FX_LOCALEDATETIMESUBCATEGORY_Short},
     39     {0xa084a381, L"medium", FX_LOCALEDATETIMESUBCATEGORY_Medium},
     40     {0xcdce56b3, L"full", FX_LOCALEDATETIMESUBCATEGORY_Full},
     41     {0xf6b4afb0, L"long", FX_LOCALEDATETIMESUBCATEGORY_Long},
     42 };
     43 const int32_t g_iFXLocaleDateTimeSubCatCount =
     44     sizeof(g_FXLocaleDateTimeSubCatData) / sizeof(FX_LOCALESUBCATEGORYINFO);
     45 
     46 const FX_LOCALESUBCATEGORYINFO g_FXLocaleNumSubCatData[] = {
     47     {0x46f95531, L"percent", FX_LOCALENUMPATTERN_Percent},
     48     {0x4c4e8acb, L"currency", FX_LOCALENUMPATTERN_Currency},
     49     {0x54034c2f, L"decimal", FX_LOCALENUMPATTERN_Decimal},
     50     {0x7568e6ae, L"integer", FX_LOCALENUMPATTERN_Integer},
     51 };
     52 const int32_t g_iFXLocaleNumSubCatCount =
     53     sizeof(g_FXLocaleNumSubCatData) / sizeof(FX_LOCALESUBCATEGORYINFO);
     54 
     55 struct FX_LOCALETIMEZONEINFO {
     56   const wchar_t* name;
     57   int16_t iHour;
     58   int16_t iMinute;
     59 };
     60 
     61 const FX_LOCALETIMEZONEINFO g_FXLocaleTimeZoneData[] = {
     62     {L"CDT", -5, 0}, {L"CST", -6, 0}, {L"EDT", -4, 0}, {L"EST", -5, 0},
     63     {L"MDT", -6, 0}, {L"MST", -7, 0}, {L"PDT", -7, 0}, {L"PST", -8, 0},
     64 };
     65 
     66 const wchar_t gs_wsTimeSymbols[] = L"hHkKMSFAzZ";
     67 const wchar_t gs_wsDateSymbols[] = L"DJMEeGgYwW";
     68 const wchar_t gs_wsConstChars[] = L",-:/. ";
     69 
     70 int32_t ParseTimeZone(const wchar_t* pStr, int32_t iLen, FX_TIMEZONE* tz) {
     71   tz->tzHour = 0;
     72   tz->tzMinute = 0;
     73   if (iLen < 0)
     74     return 0;
     75 
     76   int32_t iStart = 1;
     77   int32_t iEnd = iStart + 2;
     78   while (iStart < iLen && iStart < iEnd)
     79     tz->tzHour = tz->tzHour * 10 + FXSYS_DecimalCharToInt(pStr[iStart++]);
     80 
     81   if (iStart < iLen && pStr[iStart] == ':')
     82     iStart++;
     83 
     84   iEnd = iStart + 2;
     85   while (iStart < iLen && iStart < iEnd)
     86     tz->tzMinute = tz->tzMinute * 10 + FXSYS_DecimalCharToInt(pStr[iStart++]);
     87 
     88   if (pStr[0] == '-')
     89     tz->tzHour = -tz->tzHour;
     90 
     91   return iStart;
     92 }
     93 
     94 int32_t ConvertHex(int32_t iKeyValue, wchar_t ch) {
     95   if (FXSYS_isHexDigit(ch))
     96     return iKeyValue * 16 + FXSYS_HexCharToInt(ch);
     97   return iKeyValue;
     98 }
     99 
    100 WideString GetLiteralText(const wchar_t* pStrPattern,
    101                           int32_t* iPattern,
    102                           int32_t iLenPattern) {
    103   WideString wsOutput;
    104   if (pStrPattern[*iPattern] != '\'')
    105     return wsOutput;
    106 
    107   (*iPattern)++;
    108   int32_t iQuote = 1;
    109   while (*iPattern < iLenPattern) {
    110     if (pStrPattern[*iPattern] == '\'') {
    111       iQuote++;
    112       if ((*iPattern + 1 >= iLenPattern) ||
    113           ((pStrPattern[*iPattern + 1] != '\'') && (iQuote % 2 == 0))) {
    114         break;
    115       }
    116       iQuote++;
    117       (*iPattern)++;
    118     } else if (pStrPattern[*iPattern] == '\\' &&
    119                (*iPattern + 1 < iLenPattern) &&
    120                pStrPattern[*iPattern + 1] == 'u') {
    121       int32_t iKeyValue = 0;
    122       *iPattern += 2;
    123       int32_t i = 0;
    124       while (*iPattern < iLenPattern && i++ < 4) {
    125         wchar_t ch = pStrPattern[(*iPattern)++];
    126         iKeyValue = ConvertHex(iKeyValue, ch);
    127       }
    128       if (iKeyValue != 0)
    129         wsOutput += static_cast<wchar_t>(iKeyValue & 0x0000FFFF);
    130 
    131       continue;
    132     }
    133     wsOutput += pStrPattern[(*iPattern)++];
    134   }
    135   return wsOutput;
    136 }
    137 
    138 WideString GetLiteralTextReverse(const wchar_t* pStrPattern,
    139                                  int32_t* iPattern) {
    140   WideString wsOutput;
    141   if (pStrPattern[*iPattern] != '\'')
    142     return wsOutput;
    143 
    144   (*iPattern)--;
    145   int32_t iQuote = 1;
    146   while (*iPattern >= 0) {
    147     if (pStrPattern[*iPattern] == '\'') {
    148       iQuote++;
    149       if (*iPattern - 1 >= 0 ||
    150           ((pStrPattern[*iPattern - 1] != '\'') && (iQuote % 2 == 0))) {
    151         break;
    152       }
    153       iQuote++;
    154       (*iPattern)--;
    155     } else if (pStrPattern[*iPattern] == '\\' &&
    156                pStrPattern[*iPattern + 1] == 'u') {
    157       (*iPattern)--;
    158       int32_t iKeyValue = 0;
    159       int32_t iLen = wsOutput.GetLength();
    160       int32_t i = 1;
    161       for (; i < iLen && i < 5; i++) {
    162         wchar_t ch = wsOutput[i];
    163         iKeyValue = ConvertHex(iKeyValue, ch);
    164       }
    165       if (iKeyValue != 0) {
    166         wsOutput.Delete(0, i);
    167         wsOutput = (wchar_t)(iKeyValue & 0x0000FFFF) + wsOutput;
    168       }
    169       continue;
    170     }
    171     wsOutput = pStrPattern[(*iPattern)--] + wsOutput;
    172   }
    173   return wsOutput;
    174 }
    175 
    176 bool GetNumericDotIndex(const WideString& wsNum,
    177                         const WideString& wsDotSymbol,
    178                         int32_t* iDotIndex) {
    179   int32_t ccf = 0;
    180   int32_t iLenf = wsNum.GetLength();
    181   const wchar_t* pStr = wsNum.c_str();
    182   int32_t iLenDot = wsDotSymbol.GetLength();
    183   while (ccf < iLenf) {
    184     if (pStr[ccf] == '\'') {
    185       GetLiteralText(pStr, &ccf, iLenf);
    186     } else if (ccf + iLenDot <= iLenf &&
    187                !wcsncmp(pStr + ccf, wsDotSymbol.c_str(), iLenDot)) {
    188       *iDotIndex = ccf;
    189       return true;
    190     }
    191     ccf++;
    192   }
    193   auto result = wsNum.Find('.');
    194   *iDotIndex = result.value_or(iLenf);
    195   return result.has_value();
    196 }
    197 
    198 bool ExtractCountDigits(const wchar_t* str,
    199                         int len,
    200                         int count,
    201                         int* cc,
    202                         uint32_t* value) {
    203   for (int i = count; i > 0; --i) {
    204     if (*cc >= len)
    205       return false;
    206     if (!FXSYS_isDecimalDigit(str[*cc]))
    207       return false;
    208     *value = *value * 10 + FXSYS_DecimalCharToInt(str[(*cc)++]);
    209   }
    210   return true;
    211 }
    212 
    213 bool ExtractCountDigitsWithOptional(const wchar_t* str,
    214                                     int len,
    215                                     int count,
    216                                     int* cc,
    217                                     uint32_t* value) {
    218   if (!ExtractCountDigits(str, len, count, cc, value))
    219     return false;
    220   ExtractCountDigits(str, len, 1, cc, value);
    221   return true;
    222 }
    223 
    224 bool ParseLocaleDate(const WideString& wsDate,
    225                      const WideString& wsDatePattern,
    226                      IFX_Locale* pLocale,
    227                      CFX_DateTime* datetime,
    228                      int32_t* cc) {
    229   uint32_t year = 1900;
    230   uint32_t month = 1;
    231   uint32_t day = 1;
    232   int32_t ccf = 0;
    233   const wchar_t* str = wsDate.c_str();
    234   int32_t len = wsDate.GetLength();
    235   const wchar_t* strf = wsDatePattern.c_str();
    236   int32_t lenf = wsDatePattern.GetLength();
    237   WideStringView wsDateSymbols(gs_wsDateSymbols);
    238   while (*cc < len && ccf < lenf) {
    239     if (strf[ccf] == '\'') {
    240       WideString wsLiteral = GetLiteralText(strf, &ccf, lenf);
    241       int32_t iLiteralLen = wsLiteral.GetLength();
    242       if (*cc + iLiteralLen > len ||
    243           wcsncmp(str + *cc, wsLiteral.c_str(), iLiteralLen)) {
    244         return false;
    245       }
    246       *cc += iLiteralLen;
    247       ccf++;
    248       continue;
    249     }
    250     if (!wsDateSymbols.Contains(strf[ccf])) {
    251       if (strf[ccf] != str[*cc])
    252         return false;
    253       (*cc)++;
    254       ccf++;
    255       continue;
    256     }
    257 
    258     WideString symbol;
    259     symbol.Reserve(4);
    260     symbol += strf[ccf++];
    261     while (ccf < lenf && strf[ccf] == symbol[0])
    262       symbol += strf[ccf++];
    263 
    264     if (symbol == L"D" || symbol == L"DD") {
    265       day = 0;
    266       if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &day))
    267         return false;
    268     } else if (symbol == L"J") {
    269       uint32_t val = 0;
    270       ExtractCountDigits(str, len, 3, cc, &val);
    271     } else if (symbol == L"M" || symbol == L"MM") {
    272       month = 0;
    273       if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &month))
    274         return false;
    275     } else if (symbol == L"MMM" || symbol == L"MMMM") {
    276       for (uint16_t i = 0; i < 12; i++) {
    277         WideString wsMonthName = pLocale->GetMonthName(i, symbol == L"MMM");
    278         if (wsMonthName.IsEmpty())
    279           continue;
    280         if (!wcsncmp(wsMonthName.c_str(), str + *cc, wsMonthName.GetLength())) {
    281           *cc += wsMonthName.GetLength();
    282           month = i + 1;
    283           break;
    284         }
    285       }
    286     } else if (symbol == L"EEE" || symbol == L"EEEE") {
    287       for (uint16_t i = 0; i < 7; i++) {
    288         WideString wsDayName = pLocale->GetDayName(i, symbol == L"EEE");
    289         if (wsDayName.IsEmpty())
    290           continue;
    291         if (!wcsncmp(wsDayName.c_str(), str + *cc, wsDayName.GetLength())) {
    292           *cc += wsDayName.GetLength();
    293           break;
    294         }
    295       }
    296     } else if (symbol == L"YY" || symbol == L"YYYY") {
    297       if (*cc + pdfium::base::checked_cast<int32_t>(symbol.GetLength()) > len)
    298         return false;
    299 
    300       year = 0;
    301       if (!ExtractCountDigits(str, len, symbol.GetLength(), cc, &year))
    302         return false;
    303       if (symbol == L"YY") {
    304         if (year <= 29)
    305           year += 2000;
    306         else
    307           year += 1900;
    308       }
    309     } else if (symbol == L"G") {
    310       *cc += 2;
    311     } else if (symbol == L"JJJ" || symbol == L"E" || symbol == L"e" ||
    312                symbol == L"w" || symbol == L"WW") {
    313       *cc += symbol.GetLength();
    314     }
    315   }
    316   if (*cc < len)
    317     return false;
    318 
    319   datetime->SetDate(year, month, day);
    320   return !!(*cc);
    321 }
    322 
    323 void ResolveZone(FX_TIMEZONE tzDiff,
    324                  IFX_Locale* pLocale,
    325                  uint32_t* wHour,
    326                  uint32_t* wMinute) {
    327   int32_t iMinuteDiff = *wHour * 60 + *wMinute;
    328   FX_TIMEZONE tzLocale = pLocale->GetTimeZone();
    329   iMinuteDiff += tzLocale.tzHour * 60 +
    330                  (tzLocale.tzHour < 0 ? -tzLocale.tzMinute : tzLocale.tzMinute);
    331   iMinuteDiff -= tzDiff.tzHour * 60 +
    332                  (tzDiff.tzHour < 0 ? -tzDiff.tzMinute : tzDiff.tzMinute);
    333 
    334   iMinuteDiff %= 1440;
    335   if (iMinuteDiff < 0)
    336     iMinuteDiff += 1440;
    337 
    338   *wHour = iMinuteDiff / 60;
    339   *wMinute = iMinuteDiff % 60;
    340 }
    341 
    342 bool ParseLocaleTime(const WideString& wsTime,
    343                      const WideString& wsTimePattern,
    344                      IFX_Locale* pLocale,
    345                      CFX_DateTime* datetime,
    346                      int32_t* cc) {
    347   uint32_t hour = 0;
    348   uint32_t minute = 0;
    349   uint32_t second = 0;
    350   uint32_t millisecond = 0;
    351   int32_t ccf = 0;
    352   const wchar_t* str = wsTime.c_str();
    353   int len = wsTime.GetLength();
    354   const wchar_t* strf = wsTimePattern.c_str();
    355   int lenf = wsTimePattern.GetLength();
    356   bool bHasA = false;
    357   bool bPM = false;
    358   WideStringView wsTimeSymbols(gs_wsTimeSymbols);
    359   while (*cc < len && ccf < lenf) {
    360     if (strf[ccf] == '\'') {
    361       WideString wsLiteral = GetLiteralText(strf, &ccf, lenf);
    362       int32_t iLiteralLen = wsLiteral.GetLength();
    363       if (*cc + iLiteralLen > len ||
    364           wcsncmp(str + *cc, wsLiteral.c_str(), iLiteralLen)) {
    365         return false;
    366       }
    367       *cc += iLiteralLen;
    368       ccf++;
    369       continue;
    370     }
    371     if (!wsTimeSymbols.Contains(strf[ccf])) {
    372       if (strf[ccf] != str[*cc])
    373         return false;
    374       (*cc)++;
    375       ccf++;
    376       continue;
    377     }
    378 
    379     WideString symbol;
    380     symbol.Reserve(4);
    381     symbol += strf[ccf++];
    382     while (ccf < lenf && strf[ccf] == symbol[0])
    383       symbol += strf[ccf++];
    384 
    385     if (symbol == L"k" || symbol == L"K" || symbol == L"h" || symbol == L"H") {
    386       hour = 0;
    387       if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &hour))
    388         return false;
    389       if (symbol == L"K" && hour == 24)
    390         hour = 0;
    391     } else if (symbol == L"kk" || symbol == L"KK" || symbol == L"hh" ||
    392                symbol == L"HH") {
    393       hour = 0;
    394       if (!ExtractCountDigits(str, len, 2, cc, &hour))
    395         return false;
    396       if (symbol == L"KK" && hour == 24)
    397         hour = 0;
    398     } else if (symbol == L"M") {
    399       minute = 0;
    400       if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &minute))
    401         return false;
    402     } else if (symbol == L"MM") {
    403       minute = 0;
    404       if (!ExtractCountDigits(str, len, 2, cc, &minute))
    405         return false;
    406     } else if (symbol == L"S") {
    407       second = 0;
    408       if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &second))
    409         return false;
    410     } else if (symbol == L"SS") {
    411       second = 0;
    412       if (!ExtractCountDigits(str, len, 2, cc, &second))
    413         return false;
    414     } else if (symbol == L"FFF") {
    415       millisecond = 0;
    416       if (!ExtractCountDigits(str, len, 3, cc, &millisecond))
    417         return false;
    418     } else if (symbol == L"A") {
    419       WideString wsAM = pLocale->GetMeridiemName(true);
    420       WideString wsPM = pLocale->GetMeridiemName(false);
    421       if ((*cc + pdfium::base::checked_cast<int32_t>(wsAM.GetLength()) <=
    422            len) &&
    423           (WideStringView(str + *cc, wsAM.GetLength()) == wsAM)) {
    424         *cc += wsAM.GetLength();
    425         bHasA = true;
    426       } else if ((*cc + pdfium::base::checked_cast<int32_t>(wsPM.GetLength()) <=
    427                   len) &&
    428                  (WideStringView(str + *cc, wsPM.GetLength()) == wsPM)) {
    429         *cc += wsPM.GetLength();
    430         bHasA = true;
    431         bPM = true;
    432       }
    433     } else if (symbol == L"Z") {
    434       if (*cc + 3 > len)
    435         continue;
    436 
    437       WideString tz(str[(*cc)++]);
    438       tz += str[(*cc)++];
    439       tz += str[(*cc)++];
    440       if (tz == L"GMT") {
    441         FX_TIMEZONE tzDiff;
    442         tzDiff.tzHour = 0;
    443         tzDiff.tzMinute = 0;
    444         if (*cc < len && (str[*cc] == '-' || str[*cc] == '+'))
    445           *cc += ParseTimeZone(str + *cc, len - *cc, &tzDiff);
    446 
    447         ResolveZone(tzDiff, pLocale, &hour, &minute);
    448       } else {
    449         // Search the timezone list. There are only 8 of them, so linear scan.
    450         for (size_t i = 0; i < FX_ArraySize(g_FXLocaleTimeZoneData); ++i) {
    451           const FX_LOCALETIMEZONEINFO& info = g_FXLocaleTimeZoneData[i];
    452           if (tz != info.name)
    453             continue;
    454 
    455           hour += info.iHour;
    456           minute += info.iHour > 0 ? info.iMinute : -info.iMinute;
    457           break;
    458         }
    459       }
    460     } else if (symbol == L"z") {
    461       if (str[*cc] != 'Z') {
    462         FX_TIMEZONE tzDiff;
    463         *cc += ParseTimeZone(str + *cc, len - *cc, &tzDiff);
    464         ResolveZone(tzDiff, pLocale, &hour, &minute);
    465       } else {
    466         (*cc)++;
    467       }
    468     }
    469   }
    470   if (bHasA) {
    471     if (bPM) {
    472       hour += 12;
    473       if (hour == 24)
    474         hour = 12;
    475     } else {
    476       if (hour == 12)
    477         hour = 0;
    478     }
    479   }
    480   datetime->SetTime(hour, minute, second, millisecond);
    481   return !!(*cc);
    482 }
    483 
    484 int32_t GetNumTrailingLimit(const WideString& wsFormat,
    485                             int iDotPos,
    486                             bool* bTrimTailZeros) {
    487   if (iDotPos < 0)
    488     return 0;
    489 
    490   int32_t iCount = wsFormat.GetLength();
    491   int32_t iTreading = 0;
    492   for (iDotPos++; iDotPos < iCount; iDotPos++) {
    493     wchar_t wc = wsFormat[iDotPos];
    494     if (wc == L'z' || wc == L'9' || wc == 'Z') {
    495       iTreading++;
    496       *bTrimTailZeros = wc != L'9';
    497     }
    498   }
    499   return iTreading;
    500 }
    501 
    502 bool IsLeapYear(uint32_t year) {
    503   return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
    504 }
    505 
    506 bool MonthHas30Days(uint32_t month) {
    507   return month == 4 || month == 6 || month == 9 || month == 11;
    508 }
    509 
    510 bool MonthHas31Days(uint32_t month) {
    511   return month != 2 && !MonthHas30Days(month);
    512 }
    513 
    514 // |month| is 1-based. e.g. 1 means January.
    515 uint16_t GetSolarMonthDays(uint16_t year, uint16_t month) {
    516   if (month == 2)
    517     return FX_IsLeapYear(year) ? 29 : 28;
    518 
    519   return MonthHas30Days(month) ? 30 : 31;
    520 }
    521 
    522 uint16_t GetWeekDay(uint16_t year, uint16_t month, uint16_t day) {
    523   static const uint16_t month_day[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
    524   uint16_t nDays =
    525       (year - 1) % 7 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
    526   nDays += month_day[month - 1] + day;
    527   if (FX_IsLeapYear(year) && month > 2)
    528     nDays++;
    529   return nDays % 7;
    530 }
    531 
    532 uint16_t GetWeekOfMonth(uint16_t year, uint16_t month, uint16_t day) {
    533   uint16_t week_day = GetWeekDay(year, month, 1);
    534   uint16_t week_index = 0;
    535   week_index += day / 7;
    536   day = day % 7;
    537   if (week_day + day > 7)
    538     week_index++;
    539   return week_index;
    540 }
    541 
    542 uint16_t GetWeekOfYear(uint16_t year, uint16_t month, uint16_t day) {
    543   uint16_t nDays = 0;
    544   for (uint16_t i = 1; i < month; i++)
    545     nDays += GetSolarMonthDays(year, i);
    546 
    547   nDays += day;
    548   uint16_t week_day = GetWeekDay(year, 1, 1);
    549   uint16_t week_index = 1;
    550   week_index += nDays / 7;
    551   nDays = nDays % 7;
    552   if (week_day + nDays > 7)
    553     week_index++;
    554   return week_index;
    555 }
    556 
    557 WideString NumToString(size_t fmt_size, int32_t value) {
    558   return WideString::Format(
    559       fmt_size == 1 ? L"%d" : fmt_size == 2 ? L"%02d" : L"%03d", value);
    560 }
    561 
    562 WideString DateFormat(const WideString& wsDatePattern,
    563                       IFX_Locale* pLocale,
    564                       const CFX_DateTime& datetime) {
    565   WideString wsResult;
    566   int32_t year = datetime.GetYear();
    567   uint8_t month = datetime.GetMonth();
    568   uint8_t day = datetime.GetDay();
    569   int32_t ccf = 0;
    570   const wchar_t* strf = wsDatePattern.c_str();
    571   int32_t lenf = wsDatePattern.GetLength();
    572   WideStringView wsDateSymbols(gs_wsDateSymbols);
    573   while (ccf < lenf) {
    574     if (strf[ccf] == '\'') {
    575       wsResult += GetLiteralText(strf, &ccf, lenf);
    576       ccf++;
    577       continue;
    578     }
    579     if (!wsDateSymbols.Contains(strf[ccf])) {
    580       wsResult += strf[ccf++];
    581       continue;
    582     }
    583 
    584     WideString symbol;
    585     symbol.Reserve(4);
    586     symbol += strf[ccf++];
    587     while (ccf < lenf && strf[ccf] == symbol[0])
    588       symbol += strf[ccf++];
    589 
    590     if (symbol == L"D" || symbol == L"DD") {
    591       wsResult += NumToString(symbol.GetLength(), day);
    592     } else if (symbol == L"J" || symbol == L"JJJ") {
    593       uint16_t nDays = 0;
    594       for (int i = 1; i < month; i++)
    595         nDays += GetSolarMonthDays(year, i);
    596       nDays += day;
    597       wsResult += NumToString(symbol.GetLength(), nDays);
    598     } else if (symbol == L"M" || symbol == L"MM") {
    599       wsResult += NumToString(symbol.GetLength(), month);
    600     } else if (symbol == L"MMM" || symbol == L"MMMM") {
    601       wsResult += pLocale->GetMonthName(month - 1, symbol == L"MMM");
    602     } else if (symbol == L"E" || symbol == L"e") {
    603       uint16_t wWeekDay = GetWeekDay(year, month, day);
    604       wsResult += NumToString(
    605           1, symbol == L"E" ? wWeekDay + 1 : (wWeekDay ? wWeekDay : 7));
    606     } else if (symbol == L"EEE" || symbol == L"EEEE") {
    607       wsResult +=
    608           pLocale->GetDayName(GetWeekDay(year, month, day), symbol == L"EEE");
    609     } else if (symbol == L"G") {
    610       wsResult += pLocale->GetEraName(year > 0);
    611     } else if (symbol == L"YY") {
    612       wsResult += NumToString(2, year % 100);
    613     } else if (symbol == L"YYYY") {
    614       wsResult += NumToString(1, year);
    615     } else if (symbol == L"w") {
    616       wsResult += NumToString(1, GetWeekOfMonth(year, month, day));
    617     } else if (symbol == L"WW") {
    618       wsResult += NumToString(2, GetWeekOfYear(year, month, day));
    619     }
    620   }
    621   return wsResult;
    622 }
    623 
    624 WideString TimeFormat(const WideString& wsTimePattern,
    625                       IFX_Locale* pLocale,
    626                       const CFX_DateTime& datetime) {
    627   WideString wsResult;
    628   uint8_t hour = datetime.GetHour();
    629   uint8_t minute = datetime.GetMinute();
    630   uint8_t second = datetime.GetSecond();
    631   uint16_t millisecond = datetime.GetMillisecond();
    632   int32_t ccf = 0;
    633   const wchar_t* strf = wsTimePattern.c_str();
    634   int32_t lenf = wsTimePattern.GetLength();
    635   uint16_t wHour = hour;
    636   bool bPM = false;
    637   if (wsTimePattern.Contains('A')) {
    638     if (wHour >= 12)
    639       bPM = true;
    640   }
    641 
    642   WideStringView wsTimeSymbols(gs_wsTimeSymbols);
    643   while (ccf < lenf) {
    644     if (strf[ccf] == '\'') {
    645       wsResult += GetLiteralText(strf, &ccf, lenf);
    646       ccf++;
    647       continue;
    648     }
    649     if (!wsTimeSymbols.Contains(strf[ccf])) {
    650       wsResult += strf[ccf++];
    651       continue;
    652     }
    653 
    654     WideString symbol;
    655     symbol.Reserve(4);
    656     symbol += strf[ccf++];
    657     while (ccf < lenf && strf[ccf] == symbol[0])
    658       symbol += strf[ccf++];
    659 
    660     if (symbol == L"h" || symbol == L"hh") {
    661       if (wHour > 12)
    662         wHour -= 12;
    663       wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 12 : wHour);
    664     } else if (symbol == L"K" || symbol == L"KK") {
    665       wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 24 : wHour);
    666     } else if (symbol == L"k" || symbol == L"kk") {
    667       if (wHour > 12)
    668         wHour -= 12;
    669       wsResult += NumToString(symbol.GetLength(), wHour);
    670     } else if (symbol == L"H" || symbol == L"HH") {
    671       wsResult += NumToString(symbol.GetLength(), wHour);
    672     } else if (symbol == L"M" || symbol == L"MM") {
    673       wsResult += NumToString(symbol.GetLength(), minute);
    674     } else if (symbol == L"S" || symbol == L"SS") {
    675       wsResult += NumToString(symbol.GetLength(), second);
    676     } else if (symbol == L"FFF") {
    677       wsResult += NumToString(3, millisecond);
    678     } else if (symbol == L"A") {
    679       wsResult += pLocale->GetMeridiemName(!bPM);
    680     } else if (symbol == L"Z" || symbol == L"z") {
    681       if (symbol == L"Z")
    682         wsResult += L"GMT";
    683 
    684       FX_TIMEZONE tz = pLocale->GetTimeZone();
    685       if (tz.tzHour != 0 || tz.tzMinute != 0) {
    686         wsResult += tz.tzHour < 0 ? L"-" : L"+";
    687         wsResult +=
    688             WideString::Format(L"%02d:%02d", abs(tz.tzHour), tz.tzMinute);
    689       }
    690     }
    691   }
    692   return wsResult;
    693 }
    694 
    695 WideString FormatDateTimeInternal(const CFX_DateTime& dt,
    696                                   const WideString& wsDatePattern,
    697                                   const WideString& wsTimePattern,
    698                                   bool bDateFirst,
    699                                   IFX_Locale* pLocale) {
    700   WideString wsDateOut;
    701   if (!wsDatePattern.IsEmpty())
    702     wsDateOut = DateFormat(wsDatePattern, pLocale, dt);
    703 
    704   WideString wsTimeOut;
    705   if (!wsTimePattern.IsEmpty())
    706     wsTimeOut = TimeFormat(wsTimePattern, pLocale, dt);
    707 
    708   return bDateFirst ? wsDateOut + wsTimeOut : wsTimeOut + wsDateOut;
    709 }
    710 
    711 }  // namespace
    712 
    713 bool FX_DateFromCanonical(const WideString& wsDate, CFX_DateTime* datetime) {
    714   const wchar_t* str = wsDate.c_str();
    715   int len = wsDate.GetLength();
    716   if (len > 10)
    717     return false;
    718 
    719   int cc = 0;
    720   uint32_t year = 0;
    721   if (!ExtractCountDigits(str, len, 4, &cc, &year))
    722     return false;
    723   if (year < 1900)
    724     return false;
    725   if (cc >= len) {
    726     datetime->SetDate(year, 1, 1);
    727     return true;
    728   }
    729 
    730   if (str[cc] == '-')
    731     cc++;
    732 
    733   uint32_t month = 0;
    734   if (!ExtractCountDigits(str, len, 2, &cc, &month))
    735     return false;
    736   if (month > 12 || month < 1)
    737     return false;
    738   if (cc >= len) {
    739     datetime->SetDate(year, month, 1);
    740     return true;
    741   }
    742 
    743   if (str[cc] == '-')
    744     cc++;
    745 
    746   uint32_t day = 0;
    747   if (!ExtractCountDigits(str, len, 2, &cc, &day))
    748     return false;
    749   if (day < 1)
    750     return false;
    751   if ((MonthHas31Days(month) && day > 31) ||
    752       (MonthHas30Days(month) && day > 30)) {
    753     return false;
    754   }
    755   if (month == 2 && day > (IsLeapYear(year) ? 29U : 28U))
    756     return false;
    757 
    758   datetime->SetDate(year, month, day);
    759   return true;
    760 }
    761 
    762 bool FX_TimeFromCanonical(const WideStringView& wsTime,
    763                           CFX_DateTime* datetime,
    764                           IFX_Locale* pLocale) {
    765   if (wsTime.GetLength() == 0)
    766     return false;
    767 
    768   const wchar_t* str = wsTime.unterminated_c_str();
    769   int len = wsTime.GetLength();
    770 
    771   int cc = 0;
    772   uint32_t hour = 0;
    773   if (!ExtractCountDigits(str, len, 2, &cc, &hour))
    774     return false;
    775   if (hour >= 24)
    776     return false;
    777   if (cc >= len) {
    778     datetime->SetTime(hour, 0, 0, 0);
    779     return true;
    780   }
    781 
    782   if (str[cc] == ':')
    783     cc++;
    784 
    785   uint32_t minute = 0;
    786   if (!ExtractCountDigits(str, len, 2, &cc, &minute))
    787     return false;
    788   if (minute >= 60)
    789     return false;
    790 
    791   if (cc >= len) {
    792     datetime->SetTime(hour, minute, 0, 0);
    793     return true;
    794   }
    795 
    796   if (str[cc] == ':')
    797     cc++;
    798 
    799   uint32_t second = 0;
    800   uint32_t millisecond = 0;
    801   if (str[cc] != 'Z') {
    802     if (!ExtractCountDigits(str, len, 2, &cc, &second))
    803       return false;
    804     if (second >= 60)
    805       return false;
    806     if (cc < len && str[cc] == '.') {
    807       cc++;
    808       if (!ExtractCountDigits(str, len, 3, &cc, &millisecond))
    809         return false;
    810     }
    811   }
    812 
    813   // Skip until we find a + or - for the time zone.
    814   while (cc < len) {
    815     if (str[cc] == '+' || str[cc] == '-')
    816       break;
    817     ++cc;
    818   }
    819 
    820   if (cc < len) {
    821     FX_TIMEZONE tzDiff;
    822     tzDiff.tzHour = 0;
    823     tzDiff.tzMinute = 0;
    824     if (str[cc] != 'Z')
    825       cc += ParseTimeZone(str + cc, len - cc, &tzDiff);
    826 
    827     ResolveZone(tzDiff, pLocale, &hour, &minute);
    828   }
    829 
    830   datetime->SetTime(hour, minute, second, millisecond);
    831   return true;
    832 }
    833 
    834 CFGAS_FormatString::CFGAS_FormatString(CXFA_LocaleMgr* pLocaleMgr)
    835     : m_pLocaleMgr(pLocaleMgr) {}
    836 
    837 CFGAS_FormatString::~CFGAS_FormatString() {}
    838 
    839 void CFGAS_FormatString::SplitFormatString(
    840     const WideString& wsFormatString,
    841     std::vector<WideString>* wsPatterns) {
    842   int32_t iStrLen = wsFormatString.GetLength();
    843   const wchar_t* pStr = wsFormatString.c_str();
    844   const wchar_t* pToken = pStr;
    845   const wchar_t* pEnd = pStr + iStrLen;
    846   bool iQuote = false;
    847   while (true) {
    848     if (pStr >= pEnd) {
    849       wsPatterns->push_back(WideString(pToken, pStr - pToken));
    850       return;
    851     }
    852     if (*pStr == '\'') {
    853       iQuote = !iQuote;
    854     } else if (*pStr == L'|' && !iQuote) {
    855       wsPatterns->push_back(WideString(pToken, pStr - pToken));
    856       pToken = pStr + 1;
    857     }
    858     pStr++;
    859   }
    860 }
    861 
    862 FX_LOCALECATEGORY CFGAS_FormatString::GetCategory(const WideString& wsPattern) {
    863   FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
    864   int32_t ccf = 0;
    865   int32_t iLenf = wsPattern.GetLength();
    866   const wchar_t* pStr = wsPattern.c_str();
    867   bool bBraceOpen = false;
    868   WideStringView wsConstChars(gs_wsConstChars);
    869   while (ccf < iLenf) {
    870     if (pStr[ccf] == '\'') {
    871       GetLiteralText(pStr, &ccf, iLenf);
    872     } else if (!bBraceOpen && !wsConstChars.Contains(pStr[ccf])) {
    873       WideString wsCategory(pStr[ccf]);
    874       ccf++;
    875       while (true) {
    876         if (ccf == iLenf)
    877           return eCategory;
    878         if (pStr[ccf] == '.' || pStr[ccf] == '(')
    879           break;
    880         if (pStr[ccf] == '{') {
    881           bBraceOpen = true;
    882           break;
    883         }
    884         wsCategory += pStr[ccf];
    885         ccf++;
    886       }
    887 
    888       uint32_t dwHash = FX_HashCode_GetW(wsCategory.AsStringView(), false);
    889       if (dwHash == FX_LOCALECATEGORY_DateTimeHash)
    890         return FX_LOCALECATEGORY_DateTime;
    891       if (dwHash == FX_LOCALECATEGORY_TextHash)
    892         return FX_LOCALECATEGORY_Text;
    893       if (dwHash == FX_LOCALECATEGORY_NumHash)
    894         return FX_LOCALECATEGORY_Num;
    895       if (dwHash == FX_LOCALECATEGORY_ZeroHash)
    896         return FX_LOCALECATEGORY_Zero;
    897       if (dwHash == FX_LOCALECATEGORY_NullHash)
    898         return FX_LOCALECATEGORY_Null;
    899       if (dwHash == FX_LOCALECATEGORY_DateHash) {
    900         if (eCategory == FX_LOCALECATEGORY_Time)
    901           return FX_LOCALECATEGORY_DateTime;
    902         eCategory = FX_LOCALECATEGORY_Date;
    903       } else if (dwHash == FX_LOCALECATEGORY_TimeHash) {
    904         if (eCategory == FX_LOCALECATEGORY_Date)
    905           return FX_LOCALECATEGORY_DateTime;
    906         eCategory = FX_LOCALECATEGORY_Time;
    907       }
    908     } else if (pStr[ccf] == '}') {
    909       bBraceOpen = false;
    910     }
    911     ccf++;
    912   }
    913   return eCategory;
    914 }
    915 
    916 WideString CFGAS_FormatString::GetTextFormat(const WideString& wsPattern,
    917                                              const WideStringView& wsCategory) {
    918   int32_t ccf = 0;
    919   int32_t iLenf = wsPattern.GetLength();
    920   const wchar_t* pStr = wsPattern.c_str();
    921   bool bBrackOpen = false;
    922   WideStringView wsConstChars(gs_wsConstChars);
    923   WideString wsPurgePattern;
    924   while (ccf < iLenf) {
    925     if (pStr[ccf] == '\'') {
    926       int32_t iCurChar = ccf;
    927       GetLiteralText(pStr, &ccf, iLenf);
    928       wsPurgePattern += WideStringView(pStr + iCurChar, ccf - iCurChar + 1);
    929     } else if (!bBrackOpen && !wsConstChars.Contains(pStr[ccf])) {
    930       WideString wsSearchCategory(pStr[ccf]);
    931       ccf++;
    932       while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
    933              pStr[ccf] != '(') {
    934         wsSearchCategory += pStr[ccf];
    935         ccf++;
    936       }
    937       if (wsSearchCategory != wsCategory)
    938         continue;
    939 
    940       while (ccf < iLenf) {
    941         if (pStr[ccf] == '(') {
    942           ccf++;
    943           // Skip over the encoding name.
    944           while (ccf < iLenf && pStr[ccf] != ')')
    945             ccf++;
    946         } else if (pStr[ccf] == '{') {
    947           bBrackOpen = true;
    948           break;
    949         }
    950         ccf++;
    951       }
    952     } else if (pStr[ccf] != '}') {
    953       wsPurgePattern += pStr[ccf];
    954     }
    955     ccf++;
    956   }
    957   if (!bBrackOpen)
    958     wsPurgePattern = wsPattern;
    959 
    960   return wsPurgePattern;
    961 }
    962 
    963 IFX_Locale* CFGAS_FormatString::GetNumericFormat(const WideString& wsPattern,
    964                                                  int32_t* iDotIndex,
    965                                                  uint32_t* dwStyle,
    966                                                  WideString* wsPurgePattern) {
    967   *dwStyle = 0;
    968   IFX_Locale* pLocale = nullptr;
    969   int32_t ccf = 0;
    970   int32_t iLenf = wsPattern.GetLength();
    971   const wchar_t* pStr = wsPattern.c_str();
    972   bool bFindDot = false;
    973   bool bBrackOpen = false;
    974   WideStringView wsConstChars(gs_wsConstChars);
    975   while (ccf < iLenf) {
    976     if (pStr[ccf] == '\'') {
    977       int32_t iCurChar = ccf;
    978       GetLiteralText(pStr, &ccf, iLenf);
    979       *wsPurgePattern += WideStringView(pStr + iCurChar, ccf - iCurChar + 1);
    980     } else if (!bBrackOpen && !wsConstChars.Contains(pStr[ccf])) {
    981       WideString wsCategory(pStr[ccf]);
    982       ccf++;
    983       while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
    984              pStr[ccf] != '(') {
    985         wsCategory += pStr[ccf];
    986         ccf++;
    987       }
    988       if (wsCategory != L"num") {
    989         bBrackOpen = true;
    990         ccf = 0;
    991         continue;
    992       }
    993       while (ccf < iLenf) {
    994         if (pStr[ccf] == '{') {
    995           bBrackOpen = true;
    996           break;
    997         }
    998         if (pStr[ccf] == '(') {
    999           ccf++;
   1000           WideString wsLCID;
   1001           while (ccf < iLenf && pStr[ccf] != ')')
   1002             wsLCID += pStr[ccf++];
   1003 
   1004           pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
   1005         } else if (pStr[ccf] == '.') {
   1006           WideString wsSubCategory;
   1007           ccf++;
   1008           while (ccf < iLenf && pStr[ccf] != '(' && pStr[ccf] != '{')
   1009             wsSubCategory += pStr[ccf++];
   1010 
   1011           uint32_t dwSubHash =
   1012               FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
   1013           FX_LOCALENUMSUBCATEGORY eSubCategory = FX_LOCALENUMPATTERN_Decimal;
   1014           for (int32_t i = 0; i < g_iFXLocaleNumSubCatCount; i++) {
   1015             if (g_FXLocaleNumSubCatData[i].uHash == dwSubHash) {
   1016               eSubCategory = (FX_LOCALENUMSUBCATEGORY)g_FXLocaleNumSubCatData[i]
   1017                                  .eSubCategory;
   1018               break;
   1019             }
   1020           }
   1021           if (!pLocale)
   1022             pLocale = m_pLocaleMgr->GetDefLocale();
   1023 
   1024           ASSERT(pLocale);
   1025 
   1026           wsSubCategory = pLocale->GetNumPattern(eSubCategory);
   1027           auto result = wsSubCategory.Find('.');
   1028           if (result.has_value() && result.value() != 0) {
   1029             *iDotIndex += wsPurgePattern->GetLength();
   1030             bFindDot = true;
   1031             *dwStyle |= FX_NUMSTYLE_DotVorv;
   1032           }
   1033           *wsPurgePattern += wsSubCategory;
   1034           if (eSubCategory == FX_LOCALENUMPATTERN_Percent)
   1035             *dwStyle |= FX_NUMSTYLE_Percent;
   1036 
   1037           continue;
   1038         }
   1039         ccf++;
   1040       }
   1041     } else if (pStr[ccf] == 'E') {
   1042       *dwStyle |= FX_NUMSTYLE_Exponent;
   1043       *wsPurgePattern += pStr[ccf];
   1044     } else if (pStr[ccf] == '%') {
   1045       *dwStyle |= FX_NUMSTYLE_Percent;
   1046       *wsPurgePattern += pStr[ccf];
   1047     } else if (pStr[ccf] != '}') {
   1048       *wsPurgePattern += pStr[ccf];
   1049     }
   1050     if (!bFindDot) {
   1051       if (pStr[ccf] == '.' || pStr[ccf] == 'V' || pStr[ccf] == 'v') {
   1052         bFindDot = true;
   1053         *iDotIndex = wsPurgePattern->GetLength() - 1;
   1054         *dwStyle |= FX_NUMSTYLE_DotVorv;
   1055       }
   1056     }
   1057     ccf++;
   1058   }
   1059   if (!bFindDot)
   1060     *iDotIndex = wsPurgePattern->GetLength();
   1061   if (!pLocale)
   1062     pLocale = m_pLocaleMgr->GetDefLocale();
   1063   return pLocale;
   1064 }
   1065 
   1066 bool CFGAS_FormatString::ParseText(const WideString& wsSrcText,
   1067                                    const WideString& wsPattern,
   1068                                    WideString* wsValue) {
   1069   wsValue->clear();
   1070   if (wsSrcText.IsEmpty() || wsPattern.IsEmpty())
   1071     return false;
   1072 
   1073   WideString wsTextFormat = GetTextFormat(wsPattern, L"text");
   1074   if (wsTextFormat.IsEmpty())
   1075     return false;
   1076 
   1077   int32_t iText = 0;
   1078   int32_t iPattern = 0;
   1079   const wchar_t* pStrText = wsSrcText.c_str();
   1080   int32_t iLenText = wsSrcText.GetLength();
   1081   const wchar_t* pStrPattern = wsTextFormat.c_str();
   1082   int32_t iLenPattern = wsTextFormat.GetLength();
   1083   while (iPattern < iLenPattern && iText < iLenText) {
   1084     switch (pStrPattern[iPattern]) {
   1085       case '\'': {
   1086         WideString wsLiteral =
   1087             GetLiteralText(pStrPattern, &iPattern, iLenPattern);
   1088         int32_t iLiteralLen = wsLiteral.GetLength();
   1089         if (iText + iLiteralLen > iLenText ||
   1090             wcsncmp(pStrText + iText, wsLiteral.c_str(), iLiteralLen)) {
   1091           *wsValue = wsSrcText;
   1092           return false;
   1093         }
   1094         iText += iLiteralLen;
   1095         iPattern++;
   1096         break;
   1097       }
   1098       case 'A':
   1099         if (FXSYS_iswalpha(pStrText[iText])) {
   1100           *wsValue += pStrText[iText];
   1101           iText++;
   1102         }
   1103         iPattern++;
   1104         break;
   1105       case 'X':
   1106         *wsValue += pStrText[iText];
   1107         iText++;
   1108         iPattern++;
   1109         break;
   1110       case 'O':
   1111       case '0':
   1112         if (FXSYS_isDecimalDigit(pStrText[iText]) ||
   1113             FXSYS_iswalpha(pStrText[iText])) {
   1114           *wsValue += pStrText[iText];
   1115           iText++;
   1116         }
   1117         iPattern++;
   1118         break;
   1119       case '9':
   1120         if (FXSYS_isDecimalDigit(pStrText[iText])) {
   1121           *wsValue += pStrText[iText];
   1122           iText++;
   1123         }
   1124         iPattern++;
   1125         break;
   1126       default:
   1127         if (pStrPattern[iPattern] != pStrText[iText]) {
   1128           *wsValue = wsSrcText;
   1129           return false;
   1130         }
   1131         iPattern++;
   1132         iText++;
   1133         break;
   1134     }
   1135   }
   1136   return iPattern == iLenPattern && iText == iLenText;
   1137 }
   1138 
   1139 bool CFGAS_FormatString::ParseNum(const WideString& wsSrcNum,
   1140                                   const WideString& wsPattern,
   1141                                   WideString* wsValue) {
   1142   wsValue->clear();
   1143   if (wsSrcNum.IsEmpty() || wsPattern.IsEmpty())
   1144     return false;
   1145 
   1146   int32_t dot_index_f = -1;
   1147   uint32_t dwFormatStyle = 0;
   1148   WideString wsNumFormat;
   1149   IFX_Locale* pLocale =
   1150       GetNumericFormat(wsPattern, &dot_index_f, &dwFormatStyle, &wsNumFormat);
   1151   if (!pLocale || wsNumFormat.IsEmpty())
   1152     return false;
   1153 
   1154   int32_t iExponent = 0;
   1155   WideString wsDotSymbol =
   1156       pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
   1157   WideString wsGroupSymbol =
   1158       pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping);
   1159   int32_t iGroupLen = wsGroupSymbol.GetLength();
   1160   WideString wsMinus = pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus);
   1161   int32_t iMinusLen = wsMinus.GetLength();
   1162   const wchar_t* str = wsSrcNum.c_str();
   1163   int len = wsSrcNum.GetLength();
   1164   const wchar_t* strf = wsNumFormat.c_str();
   1165   int lenf = wsNumFormat.GetLength();
   1166   bool bHavePercentSymbol = false;
   1167   bool bNeg = false;
   1168   bool bReverseParse = false;
   1169   int32_t dot_index = 0;
   1170 
   1171   // If we're looking for a '.', 'V' or 'v' and the input string does not
   1172   // have a dot index for one of those, then we disable parsing the decimal.
   1173   if (!GetNumericDotIndex(wsSrcNum, wsDotSymbol, &dot_index) &&
   1174       (dwFormatStyle & FX_NUMSTYLE_DotVorv))
   1175     bReverseParse = true;
   1176 
   1177   // This parse is broken into two parts based on the '.' in the number
   1178   // (or 'V' or 'v'). |dot_index_f| is the location of the dot in the format and
   1179   // |dot_index| is the location of the dot in the number.
   1180   //
   1181   // This first while() starts at the '.' and walks backwards to the start of
   1182   // the number. The second while() walks from the dot forwards to the end of
   1183   // the decimal.
   1184 
   1185   int ccf = dot_index_f - 1;
   1186   int cc = dot_index - 1;
   1187   while (ccf >= 0 && cc >= 0) {
   1188     switch (strf[ccf]) {
   1189       case '\'': {
   1190         WideString wsLiteral = GetLiteralTextReverse(strf, &ccf);
   1191         int32_t iLiteralLen = wsLiteral.GetLength();
   1192         cc -= iLiteralLen - 1;
   1193         if (cc < 0 || wcsncmp(str + cc, wsLiteral.c_str(), iLiteralLen))
   1194           return false;
   1195 
   1196         cc--;
   1197         ccf--;
   1198         break;
   1199       }
   1200       case '9':
   1201         if (!FXSYS_isDecimalDigit(str[cc]))
   1202           return false;
   1203 
   1204         wsValue->InsertAtFront(str[cc]);
   1205         cc--;
   1206         ccf--;
   1207         break;
   1208       case 'z':
   1209       case 'Z':
   1210         if (strf[ccf] == 'z' || str[cc] != ' ') {
   1211           if (FXSYS_isDecimalDigit(str[cc])) {
   1212             wsValue->InsertAtFront(str[cc]);
   1213             cc--;
   1214           }
   1215         } else {
   1216           cc--;
   1217         }
   1218         ccf--;
   1219         break;
   1220       case 'S':
   1221       case 's':
   1222         if (str[cc] == '+' || (strf[ccf] == 'S' && str[cc] == ' ')) {
   1223           cc--;
   1224         } else {
   1225           cc -= iMinusLen - 1;
   1226           if (cc < 0 || wcsncmp(str + cc, wsMinus.c_str(), iMinusLen))
   1227             return false;
   1228 
   1229           cc--;
   1230           bNeg = true;
   1231         }
   1232         ccf--;
   1233         break;
   1234       case 'E': {
   1235         bool bExpSign = false;
   1236         while (cc >= 0) {
   1237           if (str[cc] == 'E' || str[cc] == 'e')
   1238             break;
   1239           if (FXSYS_isDecimalDigit(str[cc])) {
   1240             iExponent = iExponent + FXSYS_DecimalCharToInt(str[cc]) * 10;
   1241             cc--;
   1242             continue;
   1243           }
   1244           if (str[cc] == '+') {
   1245             cc--;
   1246             continue;
   1247           }
   1248           if (cc - iMinusLen + 1 > 0 && !wcsncmp(str + (cc - iMinusLen + 1),
   1249                                                  wsMinus.c_str(), iMinusLen)) {
   1250             bExpSign = true;
   1251             cc -= iMinusLen;
   1252             continue;
   1253           }
   1254 
   1255           return false;
   1256         }
   1257         cc--;
   1258         iExponent = bExpSign ? -iExponent : iExponent;
   1259         ccf--;
   1260         break;
   1261       }
   1262       case '$': {
   1263         WideString wsSymbol =
   1264             pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol);
   1265         int32_t iSymbolLen = wsSymbol.GetLength();
   1266         cc -= iSymbolLen - 1;
   1267         if (cc < 0 || wcsncmp(str + cc, wsSymbol.c_str(), iSymbolLen))
   1268           return false;
   1269 
   1270         cc--;
   1271         ccf--;
   1272         break;
   1273       }
   1274       case 'r':
   1275       case 'R':
   1276         if (ccf - 1 >= 0 && ((strf[ccf] == 'R' && strf[ccf - 1] == 'C') ||
   1277                              (strf[ccf] == 'r' && strf[ccf - 1] == 'c'))) {
   1278           if (strf[ccf] == 'R' && str[cc] == ' ') {
   1279             cc -= 2;
   1280           } else if (str[cc] == 'R' && cc - 1 >= 0 && str[cc - 1] == 'C') {
   1281             bNeg = true;
   1282             cc -= 2;
   1283           }
   1284           ccf -= 2;
   1285         } else {
   1286           ccf--;
   1287         }
   1288         break;
   1289       case 'b':
   1290       case 'B':
   1291         if (ccf - 1 >= 0 && ((strf[ccf] == 'B' && strf[ccf - 1] == 'D') ||
   1292                              (strf[ccf] == 'b' && strf[ccf - 1] == 'd'))) {
   1293           if (strf[ccf] == 'B' && str[cc] == ' ') {
   1294             cc -= 2;
   1295           } else if (str[cc] == 'B' && cc - 1 >= 0 && str[cc - 1] == 'D') {
   1296             bNeg = true;
   1297             cc -= 2;
   1298           }
   1299           ccf -= 2;
   1300         } else {
   1301           ccf--;
   1302         }
   1303         break;
   1304       case '%': {
   1305         WideString wsSymbol =
   1306             pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent);
   1307         int32_t iSysmbolLen = wsSymbol.GetLength();
   1308         cc -= iSysmbolLen - 1;
   1309         if (cc < 0 || wcsncmp(str + cc, wsSymbol.c_str(), iSysmbolLen))
   1310           return false;
   1311 
   1312         cc--;
   1313         ccf--;
   1314         bHavePercentSymbol = true;
   1315         break;
   1316       }
   1317       case '.':
   1318       case 'V':
   1319       case 'v':
   1320       case '8':
   1321         return false;
   1322       case ',': {
   1323         if (cc >= 0) {
   1324           cc -= iGroupLen - 1;
   1325           if (cc >= 0 &&
   1326               wcsncmp(str + cc, wsGroupSymbol.c_str(), iGroupLen) == 0) {
   1327             cc--;
   1328           } else {
   1329             cc += iGroupLen - 1;
   1330           }
   1331         }
   1332         ccf--;
   1333         break;
   1334       }
   1335       case '(':
   1336       case ')':
   1337         if (str[cc] == strf[ccf])
   1338           bNeg = true;
   1339         else if (str[cc] != L' ')
   1340           return false;
   1341 
   1342         cc--;
   1343         ccf--;
   1344         break;
   1345       default:
   1346         if (strf[ccf] != str[cc])
   1347           return false;
   1348 
   1349         cc--;
   1350         ccf--;
   1351     }
   1352   }
   1353   if (cc >= 0) {
   1354     if (str[cc] == '-') {
   1355       bNeg = true;
   1356       cc--;
   1357     }
   1358     if (cc >= 0)
   1359       return false;
   1360   }
   1361   if (dot_index < len && (dwFormatStyle & FX_NUMSTYLE_DotVorv))
   1362     *wsValue += '.';
   1363   if (!bReverseParse) {
   1364     ccf = dot_index_f + 1;
   1365     cc = (dot_index == len) ? len : dot_index + 1;
   1366     while (cc < len && ccf < lenf) {
   1367       switch (strf[ccf]) {
   1368         case '\'': {
   1369           WideString wsLiteral = GetLiteralText(strf, &ccf, lenf);
   1370           int32_t iLiteralLen = wsLiteral.GetLength();
   1371           if (cc + iLiteralLen > len ||
   1372               wcsncmp(str + cc, wsLiteral.c_str(), iLiteralLen)) {
   1373             return false;
   1374           }
   1375           cc += iLiteralLen;
   1376           ccf++;
   1377           break;
   1378         }
   1379         case '9':
   1380           if (!FXSYS_isDecimalDigit(str[cc]))
   1381             return false;
   1382 
   1383           *wsValue += str[cc];
   1384           cc++;
   1385           ccf++;
   1386           break;
   1387         case 'z':
   1388         case 'Z':
   1389           if (strf[ccf] == 'z' || str[cc] != ' ') {
   1390             if (FXSYS_isDecimalDigit(str[cc])) {
   1391               *wsValue += str[cc];
   1392               cc++;
   1393             }
   1394           } else {
   1395             cc++;
   1396           }
   1397           ccf++;
   1398           break;
   1399         case 'S':
   1400         case 's':
   1401           if (str[cc] == '+' || (strf[ccf] == 'S' && str[cc] == ' ')) {
   1402             cc++;
   1403           } else {
   1404             if (cc + iMinusLen > len ||
   1405                 wcsncmp(str + cc, wsMinus.c_str(), iMinusLen)) {
   1406               return false;
   1407             }
   1408             bNeg = true;
   1409             cc += iMinusLen;
   1410           }
   1411           ccf++;
   1412           break;
   1413         case 'E': {
   1414           if (cc >= len || (str[cc] != 'E' && str[cc] != 'e'))
   1415             return false;
   1416 
   1417           bool bExpSign = false;
   1418           cc++;
   1419           if (cc < len) {
   1420             if (str[cc] == '+') {
   1421               cc++;
   1422             } else if (str[cc] == '-') {
   1423               bExpSign = true;
   1424               cc++;
   1425             }
   1426           }
   1427           while (cc < len) {
   1428             if (!FXSYS_isDecimalDigit(str[cc]))
   1429               break;
   1430 
   1431             iExponent = iExponent * 10 + FXSYS_DecimalCharToInt(str[cc]);
   1432             cc++;
   1433           }
   1434           iExponent = bExpSign ? -iExponent : iExponent;
   1435           ccf++;
   1436           break;
   1437         }
   1438         case '$': {
   1439           WideString wsSymbol =
   1440               pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol);
   1441           int32_t iSymbolLen = wsSymbol.GetLength();
   1442           if (cc + iSymbolLen > len ||
   1443               wcsncmp(str + cc, wsSymbol.c_str(), iSymbolLen)) {
   1444             return false;
   1445           }
   1446           cc += iSymbolLen;
   1447           ccf++;
   1448           break;
   1449         }
   1450         case 'c':
   1451         case 'C':
   1452           if (ccf + 1 < lenf && ((strf[ccf] == 'C' && strf[ccf + 1] == 'R') ||
   1453                                  (strf[ccf] == 'c' && strf[ccf + 1] == 'r'))) {
   1454             if (strf[ccf] == 'C' && str[cc] == ' ') {
   1455               cc++;
   1456             } else if (str[cc] == 'C' && cc + 1 < len && str[cc + 1] == 'R') {
   1457               bNeg = true;
   1458               cc += 2;
   1459             }
   1460             ccf += 2;
   1461           }
   1462           break;
   1463         case 'd':
   1464         case 'D':
   1465           if (ccf + 1 < lenf && ((strf[ccf] == 'D' && strf[ccf + 1] == 'B') ||
   1466                                  (strf[ccf] == 'd' && strf[ccf + 1] == 'b'))) {
   1467             if (strf[ccf] == 'D' && str[cc] == ' ') {
   1468               cc++;
   1469             } else if (str[cc] == 'D' && cc + 1 < len && str[cc + 1] == 'B') {
   1470               bNeg = true;
   1471               cc += 2;
   1472             }
   1473             ccf += 2;
   1474           }
   1475           break;
   1476         case '.':
   1477         case 'V':
   1478         case 'v':
   1479           return false;
   1480         case '%': {
   1481           WideString wsSymbol =
   1482               pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent);
   1483           int32_t iSysmbolLen = wsSymbol.GetLength();
   1484           if (cc + iSysmbolLen <= len &&
   1485               !wcsncmp(str + cc, wsSymbol.c_str(), iSysmbolLen)) {
   1486             cc += iSysmbolLen;
   1487           }
   1488           ccf++;
   1489           bHavePercentSymbol = true;
   1490         } break;
   1491         case '8': {
   1492           while (ccf < lenf && strf[ccf] == '8')
   1493             ccf++;
   1494 
   1495           while (cc < len && FXSYS_isDecimalDigit(str[cc])) {
   1496             *wsValue += str[cc];
   1497             cc++;
   1498           }
   1499         } break;
   1500         case ',': {
   1501           if (cc + iGroupLen <= len &&
   1502               wcsncmp(str + cc, wsGroupSymbol.c_str(), iGroupLen) == 0) {
   1503             cc += iGroupLen;
   1504           }
   1505           ccf++;
   1506           break;
   1507         }
   1508         case '(':
   1509         case ')':
   1510           if (str[cc] == strf[ccf])
   1511             bNeg = true;
   1512           else if (str[cc] != L' ')
   1513             return false;
   1514 
   1515           cc++;
   1516           ccf++;
   1517           break;
   1518         default:
   1519           if (strf[ccf] != str[cc])
   1520             return false;
   1521 
   1522           cc++;
   1523           ccf++;
   1524       }
   1525     }
   1526     if (cc != len)
   1527       return false;
   1528   }
   1529   if (iExponent || bHavePercentSymbol) {
   1530     CFX_Decimal decimal = CFX_Decimal(wsValue->AsStringView());
   1531     if (iExponent) {
   1532       decimal = decimal *
   1533                 CFX_Decimal(FXSYS_pow(10, static_cast<float>(iExponent)), 3);
   1534     }
   1535     if (bHavePercentSymbol)
   1536       decimal = decimal / CFX_Decimal(100);
   1537 
   1538     *wsValue = decimal;
   1539   }
   1540   if (bNeg)
   1541     wsValue->InsertAtFront(L'-');
   1542 
   1543   return true;
   1544 }
   1545 
   1546 FX_DATETIMETYPE CFGAS_FormatString::GetDateTimeFormat(
   1547     const WideString& wsPattern,
   1548     IFX_Locale** pLocale,
   1549     WideString* wsDatePattern,
   1550     WideString* wsTimePattern) {
   1551   *pLocale = nullptr;
   1552   WideString wsTempPattern;
   1553   FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
   1554   int32_t ccf = 0;
   1555   int32_t iLenf = wsPattern.GetLength();
   1556   const wchar_t* pStr = wsPattern.c_str();
   1557   int32_t iFindCategory = 0;
   1558   bool bBraceOpen = false;
   1559   WideStringView wsConstChars(gs_wsConstChars);
   1560   while (ccf < iLenf) {
   1561     if (pStr[ccf] == '\'') {
   1562       int32_t iCurChar = ccf;
   1563       GetLiteralText(pStr, &ccf, iLenf);
   1564       wsTempPattern += WideStringView(pStr + iCurChar, ccf - iCurChar + 1);
   1565     } else if (!bBraceOpen && iFindCategory != 3 &&
   1566                !wsConstChars.Contains(pStr[ccf])) {
   1567       WideString wsCategory(pStr[ccf]);
   1568       ccf++;
   1569       while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
   1570              pStr[ccf] != '(') {
   1571         if (pStr[ccf] == 'T') {
   1572           *wsDatePattern = wsPattern.Left(ccf);
   1573           *wsTimePattern = wsPattern.Right(wsPattern.GetLength() - ccf);
   1574           wsTimePattern->SetAt(0, ' ');
   1575           if (!*pLocale)
   1576             *pLocale = m_pLocaleMgr->GetDefLocale();
   1577 
   1578           return FX_DATETIMETYPE_DateTime;
   1579         }
   1580         wsCategory += pStr[ccf];
   1581         ccf++;
   1582       }
   1583       if (!(iFindCategory & 1) && wsCategory == L"date") {
   1584         iFindCategory |= 1;
   1585         eCategory = FX_LOCALECATEGORY_Date;
   1586         if (iFindCategory & 2)
   1587           iFindCategory = 4;
   1588       } else if (!(iFindCategory & 2) && wsCategory == L"time") {
   1589         iFindCategory |= 2;
   1590         eCategory = FX_LOCALECATEGORY_Time;
   1591       } else if (wsCategory == L"datetime") {
   1592         iFindCategory = 3;
   1593         eCategory = FX_LOCALECATEGORY_DateTime;
   1594       } else {
   1595         continue;
   1596       }
   1597       while (ccf < iLenf) {
   1598         if (pStr[ccf] == '{') {
   1599           bBraceOpen = true;
   1600           break;
   1601         }
   1602         if (pStr[ccf] == '(') {
   1603           ccf++;
   1604           WideString wsLCID;
   1605           while (ccf < iLenf && pStr[ccf] != ')')
   1606             wsLCID += pStr[ccf++];
   1607 
   1608           *pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
   1609         } else if (pStr[ccf] == '.') {
   1610           WideString wsSubCategory;
   1611           ccf++;
   1612           while (ccf < iLenf && pStr[ccf] != '(' && pStr[ccf] != '{')
   1613             wsSubCategory += pStr[ccf++];
   1614 
   1615           uint32_t dwSubHash =
   1616               FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
   1617           FX_LOCALEDATETIMESUBCATEGORY eSubCategory =
   1618               FX_LOCALEDATETIMESUBCATEGORY_Medium;
   1619           for (int32_t i = 0; i < g_iFXLocaleDateTimeSubCatCount; i++) {
   1620             if (g_FXLocaleDateTimeSubCatData[i].uHash == dwSubHash) {
   1621               eSubCategory =
   1622                   (FX_LOCALEDATETIMESUBCATEGORY)g_FXLocaleDateTimeSubCatData[i]
   1623                       .eSubCategory;
   1624               break;
   1625             }
   1626           }
   1627           if (!*pLocale)
   1628             *pLocale = m_pLocaleMgr->GetDefLocale();
   1629           ASSERT(*pLocale);
   1630 
   1631           switch (eCategory) {
   1632             case FX_LOCALECATEGORY_Date:
   1633               *wsDatePattern =
   1634                   wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
   1635               break;
   1636             case FX_LOCALECATEGORY_Time:
   1637               *wsTimePattern =
   1638                   wsTempPattern + (*pLocale)->GetTimePattern(eSubCategory);
   1639               break;
   1640             case FX_LOCALECATEGORY_DateTime:
   1641               *wsDatePattern =
   1642                   wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
   1643               *wsTimePattern = (*pLocale)->GetTimePattern(eSubCategory);
   1644               break;
   1645             default:
   1646               break;
   1647           }
   1648           wsTempPattern.clear();
   1649           continue;
   1650         }
   1651         ccf++;
   1652       }
   1653     } else if (pStr[ccf] == '}') {
   1654       bBraceOpen = false;
   1655       if (!wsTempPattern.IsEmpty()) {
   1656         if (eCategory == FX_LOCALECATEGORY_Time)
   1657           *wsTimePattern = wsTempPattern;
   1658         else if (eCategory == FX_LOCALECATEGORY_Date)
   1659           *wsDatePattern = wsTempPattern;
   1660 
   1661         wsTempPattern.clear();
   1662       }
   1663     } else {
   1664       wsTempPattern += pStr[ccf];
   1665     }
   1666     ccf++;
   1667   }
   1668 
   1669   if (!wsTempPattern.IsEmpty()) {
   1670     if (eCategory == FX_LOCALECATEGORY_Date)
   1671       *wsDatePattern += wsTempPattern;
   1672     else
   1673       *wsTimePattern += wsTempPattern;
   1674   }
   1675   if (!*pLocale)
   1676     *pLocale = m_pLocaleMgr->GetDefLocale();
   1677   if (!iFindCategory) {
   1678     wsTimePattern->clear();
   1679     *wsDatePattern = wsPattern;
   1680   }
   1681   return (FX_DATETIMETYPE)iFindCategory;
   1682 }
   1683 
   1684 bool CFGAS_FormatString::ParseDateTime(const WideString& wsSrcDateTime,
   1685                                        const WideString& wsPattern,
   1686                                        FX_DATETIMETYPE eDateTimeType,
   1687                                        CFX_DateTime* dtValue) {
   1688   dtValue->Reset();
   1689   if (wsSrcDateTime.IsEmpty() || wsPattern.IsEmpty())
   1690     return false;
   1691 
   1692   WideString wsDatePattern;
   1693   WideString wsTimePattern;
   1694   IFX_Locale* pLocale = nullptr;
   1695   FX_DATETIMETYPE eCategory =
   1696       GetDateTimeFormat(wsPattern, &pLocale, &wsDatePattern, &wsTimePattern);
   1697   if (!pLocale)
   1698     return false;
   1699   if (eCategory == FX_DATETIMETYPE_Unknown)
   1700     eCategory = eDateTimeType;
   1701   if (eCategory == FX_DATETIMETYPE_Unknown)
   1702     return false;
   1703   if (eCategory == FX_DATETIMETYPE_TimeDate) {
   1704     int32_t iStart = 0;
   1705     if (!ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
   1706                          &iStart)) {
   1707       return false;
   1708     }
   1709     if (!ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
   1710                          &iStart)) {
   1711       return false;
   1712     }
   1713   } else {
   1714     int32_t iStart = 0;
   1715     if ((eCategory & FX_DATETIMETYPE_Date) &&
   1716         !ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
   1717                          &iStart)) {
   1718       return false;
   1719     }
   1720     if ((eCategory & FX_DATETIMETYPE_Time) &&
   1721         !ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
   1722                          &iStart)) {
   1723       return false;
   1724     }
   1725   }
   1726   return true;
   1727 }
   1728 
   1729 bool CFGAS_FormatString::ParseZero(const WideString& wsSrcText,
   1730                                    const WideString& wsPattern) {
   1731   WideString wsTextFormat = GetTextFormat(wsPattern, L"zero");
   1732 
   1733   int32_t iText = 0;
   1734   int32_t iPattern = 0;
   1735   const wchar_t* pStrText = wsSrcText.c_str();
   1736   int32_t iLenText = wsSrcText.GetLength();
   1737   const wchar_t* pStrPattern = wsTextFormat.c_str();
   1738   int32_t iLenPattern = wsTextFormat.GetLength();
   1739   while (iPattern < iLenPattern && iText < iLenText) {
   1740     if (pStrPattern[iPattern] == '\'') {
   1741       WideString wsLiteral =
   1742           GetLiteralText(pStrPattern, &iPattern, iLenPattern);
   1743       int32_t iLiteralLen = wsLiteral.GetLength();
   1744       if (iText + iLiteralLen > iLenText ||
   1745           wcsncmp(pStrText + iText, wsLiteral.c_str(), iLiteralLen)) {
   1746         return false;
   1747       }
   1748       iText += iLiteralLen;
   1749       iPattern++;
   1750       continue;
   1751     }
   1752     if (pStrPattern[iPattern] != pStrText[iText])
   1753       return false;
   1754 
   1755     iText++;
   1756     iPattern++;
   1757   }
   1758   return iPattern == iLenPattern && iText == iLenText;
   1759 }
   1760 
   1761 bool CFGAS_FormatString::ParseNull(const WideString& wsSrcText,
   1762                                    const WideString& wsPattern) {
   1763   WideString wsTextFormat = GetTextFormat(wsPattern, L"null");
   1764 
   1765   int32_t iText = 0;
   1766   int32_t iPattern = 0;
   1767   const wchar_t* pStrText = wsSrcText.c_str();
   1768   int32_t iLenText = wsSrcText.GetLength();
   1769   const wchar_t* pStrPattern = wsTextFormat.c_str();
   1770   int32_t iLenPattern = wsTextFormat.GetLength();
   1771   while (iPattern < iLenPattern && iText < iLenText) {
   1772     if (pStrPattern[iPattern] == '\'') {
   1773       WideString wsLiteral =
   1774           GetLiteralText(pStrPattern, &iPattern, iLenPattern);
   1775       int32_t iLiteralLen = wsLiteral.GetLength();
   1776       if (iText + iLiteralLen > iLenText ||
   1777           wcsncmp(pStrText + iText, wsLiteral.c_str(), iLiteralLen)) {
   1778         return false;
   1779       }
   1780       iText += iLiteralLen;
   1781       iPattern++;
   1782       continue;
   1783     }
   1784     if (pStrPattern[iPattern] != pStrText[iText])
   1785       return false;
   1786 
   1787     iText++;
   1788     iPattern++;
   1789   }
   1790   return iPattern == iLenPattern && iText == iLenText;
   1791 }
   1792 
   1793 bool CFGAS_FormatString::FormatText(const WideString& wsSrcText,
   1794                                     const WideString& wsPattern,
   1795                                     WideString* wsOutput) {
   1796   if (wsPattern.IsEmpty())
   1797     return false;
   1798 
   1799   int32_t iLenText = wsSrcText.GetLength();
   1800   if (iLenText == 0)
   1801     return false;
   1802 
   1803   WideString wsTextFormat = GetTextFormat(wsPattern, L"text");
   1804 
   1805   int32_t iText = 0;
   1806   int32_t iPattern = 0;
   1807   const wchar_t* pStrText = wsSrcText.c_str();
   1808   const wchar_t* pStrPattern = wsTextFormat.c_str();
   1809   int32_t iLenPattern = wsTextFormat.GetLength();
   1810   while (iPattern < iLenPattern) {
   1811     switch (pStrPattern[iPattern]) {
   1812       case '\'': {
   1813         *wsOutput += GetLiteralText(pStrPattern, &iPattern, iLenPattern);
   1814         iPattern++;
   1815         break;
   1816       }
   1817       case 'A':
   1818         if (iText >= iLenText || !FXSYS_iswalpha(pStrText[iText]))
   1819           return false;
   1820 
   1821         *wsOutput += pStrText[iText++];
   1822         iPattern++;
   1823         break;
   1824       case 'X':
   1825         if (iText >= iLenText)
   1826           return false;
   1827 
   1828         *wsOutput += pStrText[iText++];
   1829         iPattern++;
   1830         break;
   1831       case 'O':
   1832       case '0':
   1833         if (iText >= iLenText || (!FXSYS_isDecimalDigit(pStrText[iText]) &&
   1834                                   !FXSYS_iswalpha(pStrText[iText]))) {
   1835           return false;
   1836         }
   1837         *wsOutput += pStrText[iText++];
   1838         iPattern++;
   1839         break;
   1840       case '9':
   1841         if (iText >= iLenText || !FXSYS_isDecimalDigit(pStrText[iText]))
   1842           return false;
   1843 
   1844         *wsOutput += pStrText[iText++];
   1845         iPattern++;
   1846         break;
   1847       default:
   1848         *wsOutput += pStrPattern[iPattern++];
   1849         break;
   1850     }
   1851   }
   1852   return iText == iLenText;
   1853 }
   1854 
   1855 bool CFGAS_FormatString::FormatStrNum(const WideStringView& wsInputNum,
   1856                                       const WideString& wsPattern,
   1857                                       WideString* wsOutput) {
   1858   if (wsInputNum.IsEmpty() || wsPattern.IsEmpty())
   1859     return false;
   1860 
   1861   int32_t dot_index_f = -1;
   1862   uint32_t dwNumStyle = 0;
   1863   WideString wsNumFormat;
   1864   IFX_Locale* pLocale =
   1865       GetNumericFormat(wsPattern, &dot_index_f, &dwNumStyle, &wsNumFormat);
   1866   if (!pLocale || wsNumFormat.IsEmpty())
   1867     return false;
   1868 
   1869   int32_t cc = 0, ccf = 0;
   1870   const wchar_t* strf = wsNumFormat.c_str();
   1871   int lenf = wsNumFormat.GetLength();
   1872   WideString wsSrcNum(wsInputNum);
   1873   wsSrcNum.TrimLeft('0');
   1874   if (wsSrcNum.IsEmpty() || wsSrcNum[0] == '.')
   1875     wsSrcNum.InsertAtFront('0');
   1876 
   1877   CFX_Decimal decimal = CFX_Decimal(wsSrcNum.AsStringView());
   1878   if (dwNumStyle & FX_NUMSTYLE_Percent) {
   1879     decimal = decimal * CFX_Decimal(100);
   1880     wsSrcNum = decimal;
   1881   }
   1882 
   1883   int32_t exponent = 0;
   1884   if (dwNumStyle & FX_NUMSTYLE_Exponent) {
   1885     int fixed_count = 0;
   1886     while (ccf < dot_index_f) {
   1887       switch (strf[ccf]) {
   1888         case '\'':
   1889           GetLiteralText(strf, &ccf, dot_index_f);
   1890           break;
   1891         case '9':
   1892         case 'z':
   1893         case 'Z':
   1894           fixed_count++;
   1895           break;
   1896       }
   1897       ccf++;
   1898     }
   1899 
   1900     int threshold = 1;
   1901     while (fixed_count > 1) {
   1902       threshold *= 10;
   1903       fixed_count--;
   1904     }
   1905     if (decimal != CFX_Decimal(0)) {
   1906       if (decimal < CFX_Decimal(threshold)) {
   1907         decimal = decimal * CFX_Decimal(10);
   1908         exponent = -1;
   1909         while (decimal < CFX_Decimal(threshold)) {
   1910           decimal = decimal * CFX_Decimal(10);
   1911           exponent -= 1;
   1912         }
   1913       } else if (decimal > CFX_Decimal(threshold)) {
   1914         threshold *= 10;
   1915         while (decimal > CFX_Decimal(threshold)) {
   1916           decimal = decimal / CFX_Decimal(10);
   1917           exponent += 1;
   1918         }
   1919       }
   1920     }
   1921   }
   1922 
   1923   bool bTrimTailZeros = false;
   1924   int32_t iTreading =
   1925       GetNumTrailingLimit(wsNumFormat, dot_index_f, &bTrimTailZeros);
   1926   int32_t scale = decimal.GetScale();
   1927   if (iTreading < scale) {
   1928     decimal.SetScale(iTreading);
   1929     wsSrcNum = decimal;
   1930   }
   1931   if (bTrimTailZeros && scale > 0 && iTreading > 0) {
   1932     wsSrcNum.TrimRight(L"0");
   1933     wsSrcNum.TrimRight(L".");
   1934   }
   1935 
   1936   WideString wsGroupSymbol =
   1937       pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping);
   1938   bool bNeg = false;
   1939   if (wsSrcNum[0] == '-') {
   1940     bNeg = true;
   1941     wsSrcNum.Delete(0, 1);
   1942   }
   1943 
   1944   bool bAddNeg = false;
   1945   const wchar_t* str = wsSrcNum.c_str();
   1946   int len = wsSrcNum.GetLength();
   1947   auto dot_index = wsSrcNum.Find('.');
   1948   if (!dot_index.has_value())
   1949     dot_index = len;
   1950 
   1951   ccf = dot_index_f - 1;
   1952   cc = dot_index.value() - 1;
   1953   while (ccf >= 0) {
   1954     switch (strf[ccf]) {
   1955       case '9':
   1956         if (cc >= 0) {
   1957           if (!FXSYS_isDecimalDigit(str[cc]))
   1958             return false;
   1959 
   1960           wsOutput->InsertAtFront(str[cc]);
   1961           cc--;
   1962         } else {
   1963           wsOutput->InsertAtFront(L'0');
   1964         }
   1965         ccf--;
   1966         break;
   1967       case 'z':
   1968         if (cc >= 0) {
   1969           if (!FXSYS_isDecimalDigit(str[cc]))
   1970             return false;
   1971           if (str[0] != '0')
   1972             wsOutput->InsertAtFront(str[cc]);
   1973 
   1974           cc--;
   1975         }
   1976         ccf--;
   1977         break;
   1978       case 'Z':
   1979         if (cc >= 0) {
   1980           if (!FXSYS_isDecimalDigit(str[cc]))
   1981             return false;
   1982 
   1983           wsOutput->InsertAtFront(str[0] == '0' ? L' ' : str[cc]);
   1984           cc--;
   1985         } else {
   1986           wsOutput->InsertAtFront(L' ');
   1987         }
   1988         ccf--;
   1989         break;
   1990       case 'S':
   1991         if (bNeg) {
   1992           *wsOutput =
   1993               pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
   1994           bAddNeg = true;
   1995         } else {
   1996           wsOutput->InsertAtFront(L' ');
   1997         }
   1998         ccf--;
   1999         break;
   2000       case 's':
   2001         if (bNeg) {
   2002           *wsOutput =
   2003               pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
   2004           bAddNeg = true;
   2005         }
   2006         ccf--;
   2007         break;
   2008       case 'E': {
   2009         *wsOutput = WideString::Format(L"E%+d", exponent) + *wsOutput;
   2010         ccf--;
   2011         break;
   2012       }
   2013       case '$': {
   2014         *wsOutput =
   2015             pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol) +
   2016             *wsOutput;
   2017         ccf--;
   2018         break;
   2019       }
   2020       case 'r':
   2021         if (ccf - 1 >= 0 && strf[ccf - 1] == 'c') {
   2022           if (bNeg)
   2023             *wsOutput = L"CR" + *wsOutput;
   2024 
   2025           ccf -= 2;
   2026           bAddNeg = true;
   2027         }
   2028         break;
   2029       case 'R':
   2030         if (ccf - 1 >= 0 && strf[ccf - 1] == 'C') {
   2031           *wsOutput = bNeg ? L"CR" : L"  " + *wsOutput;
   2032           ccf -= 2;
   2033           bAddNeg = true;
   2034         }
   2035         break;
   2036       case 'b':
   2037         if (ccf - 1 >= 0 && strf[ccf - 1] == 'd') {
   2038           if (bNeg)
   2039             *wsOutput = L"db" + *wsOutput;
   2040 
   2041           ccf -= 2;
   2042           bAddNeg = true;
   2043         }
   2044         break;
   2045       case 'B':
   2046         if (ccf - 1 >= 0 && strf[ccf - 1] == 'D') {
   2047           *wsOutput = bNeg ? L"DB" : L"  " + *wsOutput;
   2048           ccf -= 2;
   2049           bAddNeg = true;
   2050         }
   2051         break;
   2052       case '%': {
   2053         *wsOutput =
   2054             pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent) + *wsOutput;
   2055         ccf--;
   2056         break;
   2057       }
   2058       case ',':
   2059         if (cc >= 0)
   2060           *wsOutput = wsGroupSymbol + *wsOutput;
   2061 
   2062         ccf--;
   2063         break;
   2064       case '(':
   2065         wsOutput->InsertAtFront(bNeg ? L'(' : L' ');
   2066         bAddNeg = true;
   2067         ccf--;
   2068         break;
   2069       case ')':
   2070         wsOutput->InsertAtFront(bNeg ? L')' : L' ');
   2071         ccf--;
   2072         break;
   2073       case '\'':
   2074         *wsOutput = GetLiteralTextReverse(strf, &ccf) + *wsOutput;
   2075         ccf--;
   2076         break;
   2077       default:
   2078         wsOutput->InsertAtFront(strf[ccf]);
   2079         ccf--;
   2080     }
   2081   }
   2082 
   2083   if (cc >= 0) {
   2084     int nPos = dot_index.value() % 3;
   2085     wsOutput->clear();
   2086     for (int32_t i = 0;
   2087          i < pdfium::base::checked_cast<int32_t>(dot_index.value()); i++) {
   2088       if (i % 3 == nPos && i != 0)
   2089         *wsOutput += wsGroupSymbol;
   2090       *wsOutput += wsSrcNum[i];
   2091     }
   2092     if (pdfium::base::checked_cast<int32_t>(dot_index.value()) < len) {
   2093       *wsOutput += pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
   2094       *wsOutput += wsSrcNum.Right(len - dot_index.value() - 1);
   2095     }
   2096     if (bNeg) {
   2097       *wsOutput =
   2098           pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
   2099     }
   2100     return false;
   2101   }
   2102   if (dot_index_f ==
   2103       pdfium::base::checked_cast<int32_t>(wsNumFormat.GetLength())) {
   2104     if (!bAddNeg && bNeg) {
   2105       *wsOutput =
   2106           pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
   2107     }
   2108     return true;
   2109   }
   2110 
   2111   WideString wsDotSymbol =
   2112       pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
   2113   if (strf[dot_index_f] == 'V') {
   2114     *wsOutput += wsDotSymbol;
   2115   } else if (strf[dot_index_f] == '.') {
   2116     if (pdfium::base::checked_cast<int32_t>(dot_index.value()) < len)
   2117       *wsOutput += wsDotSymbol;
   2118     else if (strf[dot_index_f + 1] == '9' || strf[dot_index_f + 1] == 'Z')
   2119       *wsOutput += wsDotSymbol;
   2120   }
   2121 
   2122   ccf = dot_index_f + 1;
   2123   cc = dot_index.value() + 1;
   2124   while (ccf < lenf) {
   2125     switch (strf[ccf]) {
   2126       case '\'':
   2127         *wsOutput += GetLiteralText(strf, &ccf, lenf);
   2128         ccf++;
   2129         break;
   2130       case '9':
   2131         if (cc < len) {
   2132           if (!FXSYS_isDecimalDigit(str[cc]))
   2133             return false;
   2134 
   2135           *wsOutput += str[cc];
   2136           cc++;
   2137         } else {
   2138           *wsOutput += L'0';
   2139         }
   2140         ccf++;
   2141         break;
   2142       case 'z':
   2143         if (cc < len) {
   2144           if (!FXSYS_isDecimalDigit(str[cc]))
   2145             return false;
   2146 
   2147           *wsOutput += str[cc];
   2148           cc++;
   2149         }
   2150         ccf++;
   2151         break;
   2152       case 'Z':
   2153         if (cc < len) {
   2154           if (!FXSYS_isDecimalDigit(str[cc]))
   2155             return false;
   2156 
   2157           *wsOutput += str[cc];
   2158           cc++;
   2159         } else {
   2160           *wsOutput += L'0';
   2161         }
   2162         ccf++;
   2163         break;
   2164       case 'E': {
   2165         *wsOutput += WideString::Format(L"E%+d", exponent);
   2166         ccf++;
   2167         break;
   2168       }
   2169       case '$':
   2170         *wsOutput +=
   2171             pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol);
   2172         ccf++;
   2173         break;
   2174       case 'c':
   2175         if (ccf + 1 < lenf && strf[ccf + 1] == 'r') {
   2176           if (bNeg)
   2177             *wsOutput += L"CR";
   2178 
   2179           ccf += 2;
   2180           bAddNeg = true;
   2181         }
   2182         break;
   2183       case 'C':
   2184         if (ccf + 1 < lenf && strf[ccf + 1] == 'R') {
   2185           *wsOutput += bNeg ? L"CR" : L"  ";
   2186           ccf += 2;
   2187           bAddNeg = true;
   2188         }
   2189         break;
   2190       case 'd':
   2191         if (ccf + 1 < lenf && strf[ccf + 1] == 'b') {
   2192           if (bNeg)
   2193             *wsOutput += L"db";
   2194 
   2195           ccf += 2;
   2196           bAddNeg = true;
   2197         }
   2198         break;
   2199       case 'D':
   2200         if (ccf + 1 < lenf && strf[ccf + 1] == 'B') {
   2201           *wsOutput += bNeg ? L"DB" : L"  ";
   2202           ccf += 2;
   2203           bAddNeg = true;
   2204         }
   2205         break;
   2206       case '%':
   2207         *wsOutput += pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent);
   2208         ccf++;
   2209         break;
   2210       case '8':
   2211         while (ccf < lenf && strf[ccf] == '8')
   2212           ccf++;
   2213         while (cc < len && FXSYS_isDecimalDigit(str[cc])) {
   2214           *wsOutput += str[cc];
   2215           cc++;
   2216         }
   2217         break;
   2218       case ',':
   2219         *wsOutput += wsGroupSymbol;
   2220         ccf++;
   2221         break;
   2222       case '(':
   2223         *wsOutput += bNeg ? '(' : ' ';
   2224         bAddNeg = true;
   2225         ccf++;
   2226         break;
   2227       case ')':
   2228         *wsOutput += bNeg ? ')' : ' ';
   2229         ccf++;
   2230         break;
   2231       default:
   2232         ccf++;
   2233     }
   2234   }
   2235   if (!bAddNeg && bNeg) {
   2236     *wsOutput = pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) +
   2237                 (*wsOutput)[0] + wsOutput->Right(wsOutput->GetLength() - 1);
   2238   }
   2239   return true;
   2240 }
   2241 
   2242 bool CFGAS_FormatString::FormatNum(const WideString& wsSrcNum,
   2243                                    const WideString& wsPattern,
   2244                                    WideString* wsOutput) {
   2245   if (wsSrcNum.IsEmpty() || wsPattern.IsEmpty())
   2246     return false;
   2247   return FormatStrNum(wsSrcNum.AsStringView(), wsPattern, wsOutput);
   2248 }
   2249 
   2250 bool CFGAS_FormatString::FormatDateTime(const WideString& wsSrcDateTime,
   2251                                         const WideString& wsPattern,
   2252                                         FX_DATETIMETYPE eDateTimeType,
   2253                                         WideString* wsOutput) {
   2254   if (wsSrcDateTime.IsEmpty() || wsPattern.IsEmpty())
   2255     return false;
   2256 
   2257   WideString wsDatePattern;
   2258   WideString wsTimePattern;
   2259   IFX_Locale* pLocale = nullptr;
   2260   FX_DATETIMETYPE eCategory =
   2261       GetDateTimeFormat(wsPattern, &pLocale, &wsDatePattern, &wsTimePattern);
   2262   if (!pLocale)
   2263     return false;
   2264 
   2265   if (eCategory == FX_DATETIMETYPE_Unknown) {
   2266     if (eDateTimeType == FX_DATETIMETYPE_Time) {
   2267       wsTimePattern = wsDatePattern;
   2268       wsDatePattern.clear();
   2269     }
   2270     eCategory = eDateTimeType;
   2271   }
   2272   if (eCategory == FX_DATETIMETYPE_Unknown)
   2273     return false;
   2274 
   2275   CFX_DateTime dt;
   2276   auto iT = wsSrcDateTime.Find(L"T");
   2277   if (!iT.has_value()) {
   2278     if (eCategory == FX_DATETIMETYPE_Date &&
   2279         FX_DateFromCanonical(wsSrcDateTime, &dt)) {
   2280       *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
   2281                                          pLocale);
   2282       return true;
   2283     }
   2284     if (eCategory == FX_DATETIMETYPE_Time &&
   2285         FX_TimeFromCanonical(wsSrcDateTime.AsStringView(), &dt, pLocale)) {
   2286       *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
   2287                                          pLocale);
   2288       return true;
   2289     }
   2290   } else {
   2291     WideString wsSrcDate(wsSrcDateTime.c_str(), iT.value());
   2292     WideStringView wsSrcTime(wsSrcDateTime.c_str() + iT.value() + 1,
   2293                              wsSrcDateTime.GetLength() - iT.value() - 1);
   2294     if (wsSrcDate.IsEmpty() || wsSrcTime.IsEmpty())
   2295       return false;
   2296     if (FX_DateFromCanonical(wsSrcDate, &dt) &&
   2297         FX_TimeFromCanonical(wsSrcTime, &dt, pLocale)) {
   2298       *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern,
   2299                                          eCategory != FX_DATETIMETYPE_TimeDate,
   2300                                          pLocale);
   2301       return true;
   2302     }
   2303   }
   2304   return false;
   2305 }
   2306 
   2307 bool CFGAS_FormatString::FormatZero(const WideString& wsPattern,
   2308                                     WideString* wsOutput) {
   2309   if (wsPattern.IsEmpty())
   2310     return false;
   2311 
   2312   WideString wsTextFormat = GetTextFormat(wsPattern, L"zero");
   2313   int32_t iPattern = 0;
   2314   const wchar_t* pStrPattern = wsTextFormat.c_str();
   2315   int32_t iLenPattern = wsTextFormat.GetLength();
   2316   while (iPattern < iLenPattern) {
   2317     if (pStrPattern[iPattern] == '\'') {
   2318       *wsOutput += GetLiteralText(pStrPattern, &iPattern, iLenPattern);
   2319       iPattern++;
   2320     } else {
   2321       *wsOutput += pStrPattern[iPattern++];
   2322     }
   2323   }
   2324   return true;
   2325 }
   2326 
   2327 bool CFGAS_FormatString::FormatNull(const WideString& wsPattern,
   2328                                     WideString* wsOutput) {
   2329   if (wsPattern.IsEmpty())
   2330     return false;
   2331 
   2332   WideString wsTextFormat = GetTextFormat(wsPattern, L"null");
   2333   int32_t iPattern = 0;
   2334   const wchar_t* pStrPattern = wsTextFormat.c_str();
   2335   int32_t iLenPattern = wsTextFormat.GetLength();
   2336   while (iPattern < iLenPattern) {
   2337     if (pStrPattern[iPattern] == '\'') {
   2338       *wsOutput += GetLiteralText(pStrPattern, &iPattern, iLenPattern);
   2339       iPattern++;
   2340       continue;
   2341     }
   2342     *wsOutput += pStrPattern[iPattern++];
   2343   }
   2344   return true;
   2345 }
   2346