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