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 
ParseTimeZone(const wchar_t * pStr,int32_t iLen,FX_TIMEZONE * tz)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 
ConvertHex(int32_t iKeyValue,wchar_t ch)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 
GetLiteralText(const wchar_t * pStrPattern,int32_t * iPattern,int32_t iLenPattern)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 
GetLiteralTextReverse(const wchar_t * pStrPattern,int32_t * iPattern)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 
GetNumericDotIndex(const WideString & wsNum,const WideString & wsDotSymbol,int32_t * iDotIndex)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 
ExtractCountDigits(const wchar_t * str,int len,int count,int * cc,uint32_t * value)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 
ExtractCountDigitsWithOptional(const wchar_t * str,int len,int count,int * cc,uint32_t * value)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 
ParseLocaleDate(const WideString & wsDate,const WideString & wsDatePattern,IFX_Locale * pLocale,CFX_DateTime * datetime,int32_t * cc)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 
ResolveZone(FX_TIMEZONE tzDiff,IFX_Locale * pLocale,uint32_t * wHour,uint32_t * wMinute)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 
ParseLocaleTime(const WideString & wsTime,const WideString & wsTimePattern,IFX_Locale * pLocale,CFX_DateTime * datetime,int32_t * cc)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 
GetNumTrailingLimit(const WideString & wsFormat,int iDotPos,bool * bTrimTailZeros)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 
IsLeapYear(uint32_t year)502 bool IsLeapYear(uint32_t year) {
503   return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
504 }
505 
MonthHas30Days(uint32_t month)506 bool MonthHas30Days(uint32_t month) {
507   return month == 4 || month == 6 || month == 9 || month == 11;
508 }
509 
MonthHas31Days(uint32_t month)510 bool MonthHas31Days(uint32_t month) {
511   return month != 2 && !MonthHas30Days(month);
512 }
513 
514 // |month| is 1-based. e.g. 1 means January.
GetSolarMonthDays(uint16_t year,uint16_t month)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 
GetWeekDay(uint16_t year,uint16_t month,uint16_t day)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 
GetWeekOfMonth(uint16_t year,uint16_t month,uint16_t day)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 
GetWeekOfYear(uint16_t year,uint16_t month,uint16_t day)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 
NumToString(size_t fmt_size,int32_t value)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 
DateFormat(const WideString & wsDatePattern,IFX_Locale * pLocale,const CFX_DateTime & datetime)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 
TimeFormat(const WideString & wsTimePattern,IFX_Locale * pLocale,const CFX_DateTime & datetime)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 
FormatDateTimeInternal(const CFX_DateTime & dt,const WideString & wsDatePattern,const WideString & wsTimePattern,bool bDateFirst,IFX_Locale * pLocale)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 
FX_DateFromCanonical(const WideString & wsDate,CFX_DateTime * datetime)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 
FX_TimeFromCanonical(const WideStringView & wsTime,CFX_DateTime * datetime,IFX_Locale * pLocale)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 
CFGAS_FormatString(CXFA_LocaleMgr * pLocaleMgr)834 CFGAS_FormatString::CFGAS_FormatString(CXFA_LocaleMgr* pLocaleMgr)
835     : m_pLocaleMgr(pLocaleMgr) {}
836 
~CFGAS_FormatString()837 CFGAS_FormatString::~CFGAS_FormatString() {}
838 
SplitFormatString(const WideString & wsFormatString,std::vector<WideString> * wsPatterns)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 
GetCategory(const WideString & wsPattern)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 
GetTextFormat(const WideString & wsPattern,const WideStringView & wsCategory)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 
GetNumericFormat(const WideString & wsPattern,int32_t * iDotIndex,uint32_t * dwStyle,WideString * wsPurgePattern)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 
ParseText(const WideString & wsSrcText,const WideString & wsPattern,WideString * wsValue)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 
ParseNum(const WideString & wsSrcNum,const WideString & wsPattern,WideString * wsValue)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 
GetDateTimeFormat(const WideString & wsPattern,IFX_Locale ** pLocale,WideString * wsDatePattern,WideString * wsTimePattern)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 
ParseDateTime(const WideString & wsSrcDateTime,const WideString & wsPattern,FX_DATETIMETYPE eDateTimeType,CFX_DateTime * dtValue)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 
ParseZero(const WideString & wsSrcText,const WideString & wsPattern)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 
ParseNull(const WideString & wsSrcText,const WideString & wsPattern)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 
FormatText(const WideString & wsSrcText,const WideString & wsPattern,WideString * wsOutput)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 
FormatStrNum(const WideStringView & wsInputNum,const WideString & wsPattern,WideString * wsOutput)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 
FormatNum(const WideString & wsSrcNum,const WideString & wsPattern,WideString * wsOutput)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 
FormatDateTime(const WideString & wsSrcDateTime,const WideString & wsPattern,FX_DATETIMETYPE eDateTimeType,WideString * wsOutput)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 
FormatZero(const WideString & wsPattern,WideString * wsOutput)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 
FormatNull(const WideString & wsPattern,WideString * wsOutput)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