• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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();
731       return type;
732   }
733 }
734 
AddTimeToTimelessType(CFGAS_StringFormatter::DateTimeType type)735 CFGAS_StringFormatter::DateTimeType AddTimeToTimelessType(
736     CFGAS_StringFormatter::DateTimeType type) {
737   switch (type) {
738     case CFGAS_StringFormatter::DateTimeType::kUnknown:
739       return CFGAS_StringFormatter::DateTimeType::kTime;
740     case CFGAS_StringFormatter::DateTimeType::kDate:
741       return CFGAS_StringFormatter::DateTimeType::kDateTime;
742     default:
743       NOTREACHED();
744       return type;
745   }
746 }
747 
748 }  // namespace
749 
FX_DateFromCanonical(pdfium::span<const wchar_t> spDate,CFX_DateTime * datetime)750 bool FX_DateFromCanonical(pdfium::span<const wchar_t> spDate,
751                           CFX_DateTime* datetime) {
752   if (spDate.size() > 10)
753     return false;
754 
755   size_t cc = 0;
756   uint32_t year = 0;
757   if (!ExtractCountDigits(spDate, 4, &cc, &year))
758     return false;
759   if (year < 1900)
760     return false;
761   if (cc >= spDate.size()) {
762     datetime->SetDate(year, 1, 1);
763     return true;
764   }
765 
766   if (spDate[cc] == '-')
767     cc++;
768 
769   uint32_t month = 0;
770   if (!ExtractCountDigits(spDate, 2, &cc, &month) || month < 1 || month > 12)
771     return false;
772 
773   if (cc >= spDate.size()) {
774     datetime->SetDate(year, month, 1);
775     return true;
776   }
777 
778   if (spDate[cc] == '-')
779     cc++;
780 
781   uint32_t day = 0;
782   if (!ExtractCountDigits(spDate, 2, &cc, &day))
783     return false;
784   if (day < 1)
785     return false;
786   if ((MonthHas31Days(month) && day > 31) ||
787       (MonthHas30Days(month) && day > 30)) {
788     return false;
789   }
790   if (month == 2 && day > (IsLeapYear(year) ? 29U : 28U))
791     return false;
792 
793   datetime->SetDate(year, month, day);
794   return true;
795 }
796 
FX_TimeFromCanonical(const LocaleIface * pLocale,pdfium::span<const wchar_t> spTime,CFX_DateTime * datetime)797 bool FX_TimeFromCanonical(const LocaleIface* pLocale,
798                           pdfium::span<const wchar_t> spTime,
799                           CFX_DateTime* datetime) {
800   if (spTime.empty())
801     return false;
802 
803   size_t cc = 0;
804   uint32_t hour = 0;
805   if (!ExtractCountDigits(spTime, 2, &cc, &hour) || hour >= 24)
806     return false;
807 
808   if (cc >= spTime.size()) {
809     datetime->SetTime(hour, 0, 0, 0);
810     return true;
811   }
812 
813   if (spTime[cc] == ':')
814     cc++;
815 
816   uint32_t minute = 0;
817   if (!ExtractCountDigits(spTime, 2, &cc, &minute) || minute >= 60)
818     return false;
819 
820   if (cc >= spTime.size()) {
821     datetime->SetTime(hour, minute, 0, 0);
822     return true;
823   }
824 
825   if (spTime[cc] == ':')
826     cc++;
827 
828   uint32_t second = 0;
829   uint32_t millisecond = 0;
830   if (cc < spTime.size() && spTime[cc] != 'Z') {
831     if (!ExtractCountDigits(spTime, 2, &cc, &second) || second >= 60)
832       return false;
833 
834     if (cc < spTime.size() && spTime[cc] == '.') {
835       cc++;
836       if (!ExtractCountDigits(spTime, 3, &cc, &millisecond))
837         return false;
838     }
839   }
840 
841   // Skip until we find a + or - for the time zone.
842   while (cc < spTime.size()) {
843     if (spTime[cc] == '+' || spTime[cc] == '-')
844       break;
845     ++cc;
846   }
847 
848   if (cc < spTime.size()) {
849     int tz_diff_minutes = 0;
850     if (spTime[cc] != 'Z')
851       cc += ParseTimeZone(spTime.subspan(cc), &tz_diff_minutes);
852     ResolveZone(tz_diff_minutes, pLocale, &hour, &minute);
853   }
854 
855   datetime->SetTime(hour, minute, second, millisecond);
856   return true;
857 }
858 
CFGAS_StringFormatter(const WideString & wsPattern)859 CFGAS_StringFormatter::CFGAS_StringFormatter(const WideString& wsPattern)
860     : m_wsPattern(wsPattern), m_spPattern(m_wsPattern.span()) {}
861 
862 CFGAS_StringFormatter::~CFGAS_StringFormatter() = default;
863 
864 // static
SplitOnBars(const WideString & wsFormatString)865 std::vector<WideString> CFGAS_StringFormatter::SplitOnBars(
866     const WideString& wsFormatString) {
867   std::vector<WideString> wsPatterns;
868   pdfium::span<const wchar_t> spFormatString = wsFormatString.span();
869   size_t index = 0;
870   size_t token = 0;
871   bool bQuote = false;
872   for (; index < spFormatString.size(); ++index) {
873     if (spFormatString[index] == '\'') {
874       bQuote = !bQuote;
875     } else if (spFormatString[index] == L'|' && !bQuote) {
876       wsPatterns.emplace_back(spFormatString.data() + token, index - token);
877       token = index + 1;
878     }
879   }
880   wsPatterns.emplace_back(spFormatString.data() + token, index - token);
881   return wsPatterns;
882 }
883 
GetCategory() const884 CFGAS_StringFormatter::Category CFGAS_StringFormatter::GetCategory() const {
885   Category eCategory = Category::kUnknown;
886   size_t ccf = 0;
887   bool bBraceOpen = false;
888   while (ccf < m_spPattern.size()) {
889     if (m_spPattern[ccf] == '\'') {
890       GetLiteralText(m_spPattern, &ccf);
891     } else if (!bBraceOpen &&
892                !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
893       WideString wsCategory(m_spPattern[ccf]);
894       ccf++;
895       while (true) {
896         if (ccf >= m_spPattern.size())
897           return eCategory;
898         if (m_spPattern[ccf] == '.' || m_spPattern[ccf] == '(')
899           break;
900         if (m_spPattern[ccf] == '{') {
901           bBraceOpen = true;
902           break;
903         }
904         wsCategory += m_spPattern[ccf];
905         ccf++;
906       }
907       if (wsCategory == kDateTimeStr)
908         return Category::kDateTime;
909       if (wsCategory == kTextStr)
910         return Category::kText;
911       if (wsCategory == kNumStr)
912         return Category::kNum;
913       if (wsCategory == kZeroStr)
914         return Category::kZero;
915       if (wsCategory == kNullStr)
916         return Category::kNull;
917       if (wsCategory == kDateStr) {
918         if (eCategory == Category::kTime)
919           return Category::kDateTime;
920         eCategory = Category::kDate;
921       } else if (wsCategory == kTimeStr) {
922         if (eCategory == Category::kDate)
923           return Category::kDateTime;
924         eCategory = Category::kTime;
925       }
926     } else if (m_spPattern[ccf] == '}') {
927       bBraceOpen = false;
928     }
929     ccf++;
930   }
931   return eCategory;
932 }
933 
GetTextFormat(WideStringView wsCategory) const934 WideString CFGAS_StringFormatter::GetTextFormat(
935     WideStringView wsCategory) const {
936   size_t ccf = 0;
937   bool bBrackOpen = false;
938   WideString wsPurgePattern;
939   while (ccf < m_spPattern.size()) {
940     if (m_spPattern[ccf] == '\'') {
941       size_t iCurChar = ccf;
942       GetLiteralText(m_spPattern, &ccf);
943       wsPurgePattern +=
944           WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
945     } else if (!bBrackOpen &&
946                !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
947       WideString wsSearchCategory(m_spPattern[ccf]);
948       ccf++;
949       while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
950              m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
951         wsSearchCategory += m_spPattern[ccf];
952         ccf++;
953       }
954       if (wsSearchCategory != wsCategory)
955         continue;
956 
957       while (ccf < m_spPattern.size()) {
958         if (m_spPattern[ccf] == '(') {
959           ccf++;
960           // Skip over the encoding name.
961           while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
962             ccf++;
963         } else if (m_spPattern[ccf] == '{') {
964           bBrackOpen = true;
965           break;
966         }
967         ccf++;
968       }
969     } else if (m_spPattern[ccf] != '}') {
970       wsPurgePattern += m_spPattern[ccf];
971     }
972     ccf++;
973   }
974   if (!bBrackOpen)
975     wsPurgePattern = m_wsPattern;
976 
977   return wsPurgePattern;
978 }
979 
GetNumericFormat(LocaleMgrIface * pLocaleMgr,size_t * iDotIndex,uint32_t * dwStyle,WideString * wsPurgePattern) const980 LocaleIface* CFGAS_StringFormatter::GetNumericFormat(
981     LocaleMgrIface* pLocaleMgr,
982     size_t* iDotIndex,
983     uint32_t* dwStyle,
984     WideString* wsPurgePattern) const {
985   *dwStyle = 0;
986   LocaleIface* pLocale = nullptr;
987   size_t ccf = 0;
988   bool bFindDot = false;
989   bool bBrackOpen = false;
990   while (ccf < m_spPattern.size()) {
991     if (m_spPattern[ccf] == '\'') {
992       size_t iCurChar = ccf;
993       GetLiteralText(m_spPattern, &ccf);
994       *wsPurgePattern +=
995           WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
996     } else if (!bBrackOpen &&
997                !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
998       WideString wsCategory(m_spPattern[ccf]);
999       ccf++;
1000       while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
1001              m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
1002         wsCategory += m_spPattern[ccf];
1003         ccf++;
1004       }
1005       if (!wsCategory.EqualsASCII("num")) {
1006         bBrackOpen = true;
1007         ccf = 0;
1008         continue;
1009       }
1010       while (ccf < m_spPattern.size()) {
1011         if (m_spPattern[ccf] == '{') {
1012           bBrackOpen = true;
1013           break;
1014         }
1015         if (m_spPattern[ccf] == '(') {
1016           ccf++;
1017           WideString wsLCID;
1018           while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
1019             wsLCID += m_spPattern[ccf++];
1020 
1021           pLocale = pLocaleMgr->GetLocaleByName(wsLCID);
1022         } else if (m_spPattern[ccf] == '.') {
1023           WideString wsSubCategory;
1024           ccf++;
1025           while (ccf < m_spPattern.size() && m_spPattern[ccf] != '(' &&
1026                  m_spPattern[ccf] != '{') {
1027             wsSubCategory += m_spPattern[ccf++];
1028           }
1029           uint32_t dwSubHash = FX_HashCode_GetW(wsSubCategory.AsStringView());
1030           LocaleIface::NumSubcategory eSubCategory =
1031               LocaleIface::NumSubcategory::kDecimal;
1032           for (const auto& data : kLocaleNumSubcategoryData) {
1033             if (data.uHash == dwSubHash) {
1034               eSubCategory = data.eSubCategory;
1035               break;
1036             }
1037           }
1038           if (!pLocale)
1039             pLocale = pLocaleMgr->GetDefLocale();
1040 
1041           wsSubCategory = pLocale->GetNumPattern(eSubCategory);
1042           auto result = wsSubCategory.Find('.');
1043           if (result.has_value() && result.value() != 0) {
1044             if (!bFindDot)
1045               *iDotIndex = wsPurgePattern->GetLength() + result.value();
1046             bFindDot = true;
1047             *dwStyle |= FX_NUMSTYLE_DotVorv;
1048           }
1049           *wsPurgePattern += wsSubCategory;
1050           if (eSubCategory == LocaleIface::NumSubcategory::kPercent)
1051             *dwStyle |= FX_NUMSTYLE_Percent;
1052           continue;
1053         }
1054         ccf++;
1055       }
1056     } else if (m_spPattern[ccf] == 'E') {
1057       *dwStyle |= FX_NUMSTYLE_Exponent;
1058       *wsPurgePattern += m_spPattern[ccf];
1059     } else if (m_spPattern[ccf] == '%') {
1060       *dwStyle |= FX_NUMSTYLE_Percent;
1061       *wsPurgePattern += m_spPattern[ccf];
1062     } else if (m_spPattern[ccf] != '}') {
1063       *wsPurgePattern += m_spPattern[ccf];
1064     }
1065     if (!bFindDot && ccf < m_spPattern.size() &&
1066         (m_spPattern[ccf] == '.' || m_spPattern[ccf] == 'V' ||
1067          m_spPattern[ccf] == 'v')) {
1068       bFindDot = true;
1069       *iDotIndex = wsPurgePattern->GetLength() - 1;
1070       *dwStyle |= FX_NUMSTYLE_DotVorv;
1071     }
1072     ccf++;
1073   }
1074   if (!bFindDot)
1075     *iDotIndex = wsPurgePattern->GetLength();
1076   if (!pLocale)
1077     pLocale = pLocaleMgr->GetDefLocale();
1078   return pLocale;
1079 }
1080 
ParseText(const WideString & wsSrcText,WideString * wsValue) const1081 bool CFGAS_StringFormatter::ParseText(const WideString& wsSrcText,
1082                                       WideString* wsValue) const {
1083   wsValue->clear();
1084   if (wsSrcText.IsEmpty() || m_spPattern.empty())
1085     return false;
1086 
1087   WideString wsTextFormat = GetTextFormat(L"text");
1088   if (wsTextFormat.IsEmpty())
1089     return false;
1090 
1091   pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1092   pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1093 
1094   size_t iText = 0;
1095   size_t iPattern = 0;
1096   while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
1097     switch (spTextFormat[iPattern]) {
1098       case '\'': {
1099         WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
1100         size_t iLiteralLen = wsLiteral.GetLength();
1101         if (iText + iLiteralLen > spSrcText.size() ||
1102             wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen) !=
1103                 0) {
1104           *wsValue = wsSrcText;
1105           return false;
1106         }
1107         iText += iLiteralLen;
1108         iPattern++;
1109         break;
1110       }
1111       case 'A':
1112         if (FXSYS_iswalpha(spSrcText[iText])) {
1113           *wsValue += spSrcText[iText];
1114           iText++;
1115         }
1116         iPattern++;
1117         break;
1118       case 'X':
1119         *wsValue += spSrcText[iText];
1120         iText++;
1121         iPattern++;
1122         break;
1123       case 'O':
1124       case '0':
1125         if (FXSYS_IsDecimalDigit(spSrcText[iText]) ||
1126             FXSYS_iswalpha(spSrcText[iText])) {
1127           *wsValue += spSrcText[iText];
1128           iText++;
1129         }
1130         iPattern++;
1131         break;
1132       case '9':
1133         if (FXSYS_IsDecimalDigit(spSrcText[iText])) {
1134           *wsValue += spSrcText[iText];
1135           iText++;
1136         }
1137         iPattern++;
1138         break;
1139       default:
1140         if (spTextFormat[iPattern] != spSrcText[iText]) {
1141           *wsValue = wsSrcText;
1142           return false;
1143         }
1144         iPattern++;
1145         iText++;
1146         break;
1147     }
1148   }
1149   return iPattern == spTextFormat.size() && iText == spSrcText.size();
1150 }
1151 
ParseNum(LocaleMgrIface * pLocaleMgr,const WideString & wsSrcNum,WideString * wsValue) const1152 bool CFGAS_StringFormatter::ParseNum(LocaleMgrIface* pLocaleMgr,
1153                                      const WideString& wsSrcNum,
1154                                      WideString* wsValue) const {
1155   wsValue->clear();
1156   if (wsSrcNum.IsEmpty() || m_spPattern.empty())
1157     return false;
1158 
1159   size_t dot_index_f = m_spPattern.size();
1160   uint32_t dwFormatStyle = 0;
1161   WideString wsNumFormat;
1162   LocaleIface* pLocale =
1163       GetNumericFormat(pLocaleMgr, &dot_index_f, &dwFormatStyle, &wsNumFormat);
1164   if (!pLocale || wsNumFormat.IsEmpty())
1165     return false;
1166 
1167   int32_t iExponent = 0;
1168   WideString wsDotSymbol = pLocale->GetDecimalSymbol();
1169   WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
1170   WideString wsMinus = pLocale->GetMinusSymbol();
1171   size_t iGroupLen = wsGroupSymbol.GetLength();
1172   size_t iMinusLen = wsMinus.GetLength();
1173 
1174   pdfium::span<const wchar_t> spSrcNum = wsSrcNum.span();
1175   pdfium::span<const wchar_t> spNumFormat = wsNumFormat.span();
1176 
1177   bool bHavePercentSymbol = false;
1178   bool bNeg = false;
1179   bool bReverseParse = false;
1180   size_t dot_index = 0;
1181 
1182   // If we're looking for a '.', 'V' or 'v' and the input string does not
1183   // have a dot index for one of those, then we disable parsing the decimal.
1184   if (!GetNumericDotIndex(wsSrcNum, wsDotSymbol, &dot_index) &&
1185       (dwFormatStyle & FX_NUMSTYLE_DotVorv))
1186     bReverseParse = true;
1187 
1188   // This parse is broken into two parts based on the '.' in the number
1189   // (or 'V' or 'v'). |dot_index_f| is the location of the dot in the format and
1190   // |dot_index| is the location of the dot in the number.
1191   //
1192   // This first while() starts at the '.' and walks backwards to the start of
1193   // the number. The second while() walks from the dot forwards to the end of
1194   // the decimal.
1195 
1196   size_t cc = dot_index - 1;
1197   size_t ccf = dot_index_f - 1;
1198   while (ccf < spNumFormat.size() && cc < spSrcNum.size()) {
1199     switch (spNumFormat[ccf]) {
1200       case '\'': {
1201         WideString wsLiteral = GetLiteralTextReverse(spNumFormat, &ccf);
1202         size_t iLiteralLen = wsLiteral.GetLength();
1203         cc -= iLiteralLen - 1;
1204         if (cc >= spSrcNum.size() ||
1205             wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
1206                 0) {
1207           return false;
1208         }
1209         cc--;
1210         ccf--;
1211         break;
1212       }
1213       case '9':
1214         if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1215           return false;
1216 
1217         wsValue->InsertAtFront(spSrcNum[cc]);
1218         cc--;
1219         ccf--;
1220         break;
1221       case 'z':
1222       case 'Z':
1223         if (spNumFormat[ccf] == 'z' || spSrcNum[cc] != ' ') {
1224           if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1225             wsValue->InsertAtFront(spSrcNum[cc]);
1226             cc--;
1227           }
1228         } else {
1229           cc--;
1230         }
1231         ccf--;
1232         break;
1233       case 'S':
1234       case 's':
1235         if (spSrcNum[cc] == '+' ||
1236             (spNumFormat[ccf] == 'S' && spSrcNum[cc] == ' ')) {
1237           cc--;
1238         } else {
1239           cc -= iMinusLen - 1;
1240           if (cc >= spSrcNum.size() ||
1241               wcsncmp(spSrcNum.data() + cc, wsMinus.c_str(), iMinusLen) != 0) {
1242             return false;
1243           }
1244           cc--;
1245           bNeg = true;
1246         }
1247         ccf--;
1248         break;
1249       case 'E': {
1250         iExponent = 0;
1251         bool bExpSign = false;
1252         while (cc < spSrcNum.size()) {
1253           if (spSrcNum[cc] == 'E' || spSrcNum[cc] == 'e')
1254             break;
1255           if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1256             if (iExponent > std::numeric_limits<int>::max() / 10)
1257               return false;
1258             iExponent = iExponent + FXSYS_DecimalCharToInt(spSrcNum[cc]) * 10;
1259             cc--;
1260             continue;
1261           }
1262           if (spSrcNum[cc] == '+') {
1263             cc--;
1264             continue;
1265           }
1266           if (cc - iMinusLen + 1 <= spSrcNum.size() &&
1267               wcsncmp(spSrcNum.data() + (cc - iMinusLen + 1), wsMinus.c_str(),
1268                       iMinusLen) == 0) {
1269             bExpSign = true;
1270             cc -= iMinusLen;
1271             continue;
1272           }
1273 
1274           return false;
1275         }
1276         cc--;
1277         iExponent = bExpSign ? -iExponent : iExponent;
1278         ccf--;
1279         break;
1280       }
1281       case '$': {
1282         WideString wsSymbol = pLocale->GetCurrencySymbol();
1283         size_t iSymbolLen = wsSymbol.GetLength();
1284         cc -= iSymbolLen - 1;
1285         if (cc >= spSrcNum.size() ||
1286             wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) != 0) {
1287           return false;
1288         }
1289         cc--;
1290         ccf--;
1291         break;
1292       }
1293       case 'r':
1294       case 'R':
1295         if (ccf - 1 < spNumFormat.size() &&
1296             ((spNumFormat[ccf] == 'R' && spNumFormat[ccf - 1] == 'C') ||
1297              (spNumFormat[ccf] == 'r' && spNumFormat[ccf - 1] == 'c'))) {
1298           if (spNumFormat[ccf] == 'R' && spSrcNum[cc] == ' ') {
1299             cc -= 2;
1300           } else if (spSrcNum[cc] == 'R' && cc - 1 < spSrcNum.size() &&
1301                      spSrcNum[cc - 1] == 'C') {
1302             bNeg = true;
1303             cc -= 2;
1304           }
1305           ccf -= 2;
1306         } else {
1307           ccf--;
1308         }
1309         break;
1310       case 'b':
1311       case 'B':
1312         if (ccf - 1 < spNumFormat.size() &&
1313             ((spNumFormat[ccf] == 'B' && spNumFormat[ccf - 1] == 'D') ||
1314              (spNumFormat[ccf] == 'b' && spNumFormat[ccf - 1] == 'd'))) {
1315           if (spNumFormat[ccf] == 'B' && spSrcNum[cc] == ' ') {
1316             cc -= 2;
1317           } else if (spSrcNum[cc] == 'B' && cc - 1 < spSrcNum.size() &&
1318                      spSrcNum[cc - 1] == 'D') {
1319             bNeg = true;
1320             cc -= 2;
1321           }
1322           ccf -= 2;
1323         } else {
1324           ccf--;
1325         }
1326         break;
1327       case '%': {
1328         WideString wsSymbol = pLocale->GetPercentSymbol();
1329         size_t iSymbolLen = wsSymbol.GetLength();
1330         cc -= iSymbolLen - 1;
1331         if (cc >= spSrcNum.size() ||
1332             wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) != 0) {
1333           return false;
1334         }
1335         cc--;
1336         ccf--;
1337         bHavePercentSymbol = true;
1338         break;
1339       }
1340       case '.':
1341       case 'V':
1342       case 'v':
1343       case '8':
1344         return false;
1345       case ',': {
1346         if (cc < spSrcNum.size()) {
1347           cc -= iGroupLen - 1;
1348           if (cc < spSrcNum.size() &&
1349               wcsncmp(spSrcNum.data() + cc, wsGroupSymbol.c_str(), iGroupLen) ==
1350                   0) {
1351             cc--;
1352           } else {
1353             cc += iGroupLen - 1;
1354           }
1355         }
1356         ccf--;
1357         break;
1358       }
1359       case '(':
1360       case ')':
1361         if (spSrcNum[cc] == spNumFormat[ccf])
1362           bNeg = true;
1363         else if (spSrcNum[cc] != L' ')
1364           return false;
1365 
1366         cc--;
1367         ccf--;
1368         break;
1369       default:
1370         if (spNumFormat[ccf] != spSrcNum[cc])
1371           return false;
1372 
1373         cc--;
1374         ccf--;
1375     }
1376   }
1377   if (cc < spSrcNum.size()) {
1378     if (spSrcNum[cc] == '-') {
1379       bNeg = true;
1380       cc--;
1381     }
1382     if (cc < spSrcNum.size())
1383       return false;
1384   }
1385   if ((dwFormatStyle & FX_NUMSTYLE_DotVorv) && dot_index < spSrcNum.size())
1386     *wsValue += '.';
1387 
1388   if (!bReverseParse) {
1389     cc = (dot_index == spSrcNum.size()) ? spSrcNum.size() : dot_index + 1;
1390     for (ccf = dot_index_f + 1;
1391          cc < spSrcNum.size() && ccf < spNumFormat.size(); ++ccf) {
1392       switch (spNumFormat[ccf]) {
1393         case '\'': {
1394           WideString wsLiteral = GetLiteralText(spNumFormat, &ccf);
1395           size_t iLiteralLen = wsLiteral.GetLength();
1396           if (cc + iLiteralLen > spSrcNum.size() ||
1397               wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
1398                   0) {
1399             return false;
1400           }
1401           cc += iLiteralLen;
1402           break;
1403         }
1404         case '9':
1405           if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1406             return false;
1407 
1408           *wsValue += spSrcNum[cc];
1409           cc++;
1410           break;
1411         case 'z':
1412         case 'Z':
1413           if (spNumFormat[ccf] == 'z' || spSrcNum[cc] != ' ') {
1414             if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1415               *wsValue += spSrcNum[cc];
1416               cc++;
1417             }
1418           } else {
1419             cc++;
1420           }
1421           break;
1422         case 'S':
1423         case 's':
1424           if (spSrcNum[cc] == '+' ||
1425               (spNumFormat[ccf] == 'S' && spSrcNum[cc] == ' ')) {
1426             cc++;
1427           } else {
1428             if (cc + iMinusLen > spSrcNum.size() ||
1429                 wcsncmp(spSrcNum.data() + cc, wsMinus.c_str(), iMinusLen) !=
1430                     0) {
1431               return false;
1432             }
1433             bNeg = true;
1434             cc += iMinusLen;
1435           }
1436           break;
1437         case 'E': {
1438           if (cc >= spSrcNum.size() ||
1439               (spSrcNum[cc] != 'E' && spSrcNum[cc] != 'e')) {
1440             return false;
1441           }
1442           iExponent = 0;
1443           bool bExpSign = false;
1444           cc++;
1445           if (cc < spSrcNum.size()) {
1446             if (spSrcNum[cc] == '+') {
1447               cc++;
1448             } else if (spSrcNum[cc] == '-') {
1449               bExpSign = true;
1450               cc++;
1451             }
1452           }
1453           while (cc < spSrcNum.size()) {
1454             if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1455               break;
1456             int digit = FXSYS_DecimalCharToInt(spSrcNum[cc]);
1457             if (iExponent > (std::numeric_limits<int>::max() - digit) / 10)
1458               return false;
1459             iExponent = iExponent * 10 + digit;
1460             cc++;
1461           }
1462           iExponent = bExpSign ? -iExponent : iExponent;
1463           break;
1464         }
1465         case '$': {
1466           WideString wsSymbol = pLocale->GetCurrencySymbol();
1467           size_t iSymbolLen = wsSymbol.GetLength();
1468           if (cc + iSymbolLen > spSrcNum.size() ||
1469               wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) !=
1470                   0) {
1471             return false;
1472           }
1473           cc += iSymbolLen;
1474           break;
1475         }
1476         case 'c':
1477         case 'C':
1478           if (ccf + 1 < spNumFormat.size() &&
1479               ((spNumFormat[ccf] == 'C' && spNumFormat[ccf + 1] == 'R') ||
1480                (spNumFormat[ccf] == 'c' && spNumFormat[ccf + 1] == 'r'))) {
1481             if (spNumFormat[ccf] == 'C' && spSrcNum[cc] == ' ') {
1482               cc++;
1483             } else if (spSrcNum[cc] == 'C' && cc + 1 < spSrcNum.size() &&
1484                        spSrcNum[cc + 1] == 'R') {
1485               bNeg = true;
1486               cc += 2;
1487             }
1488             ccf++;
1489           }
1490           break;
1491         case 'd':
1492         case 'D':
1493           if (ccf + 1 < spNumFormat.size() &&
1494               ((spNumFormat[ccf] == 'D' && spNumFormat[ccf + 1] == 'B') ||
1495                (spNumFormat[ccf] == 'd' && spNumFormat[ccf + 1] == 'b'))) {
1496             if (spNumFormat[ccf] == 'D' && spSrcNum[cc] == ' ') {
1497               cc++;
1498             } else if (spSrcNum[cc] == 'D' && cc + 1 < spSrcNum.size() &&
1499                        spSrcNum[cc + 1] == 'B') {
1500               bNeg = true;
1501               cc += 2;
1502             }
1503             ccf++;
1504           }
1505           break;
1506         case '.':
1507         case 'V':
1508         case 'v':
1509           return false;
1510         case '%': {
1511           WideString wsSymbol = pLocale->GetPercentSymbol();
1512           size_t iSymbolLen = wsSymbol.GetLength();
1513           if (cc + iSymbolLen <= spSrcNum.size() &&
1514               wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) ==
1515                   0) {
1516             cc += iSymbolLen;
1517           }
1518           bHavePercentSymbol = true;
1519         } break;
1520         case '8': {
1521           while (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == '8')
1522             ccf++;
1523 
1524           while (cc < spSrcNum.size() && FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1525             *wsValue += spSrcNum[cc];
1526             cc++;
1527           }
1528         } break;
1529         case ',': {
1530           if (cc + iGroupLen <= spSrcNum.size() &&
1531               wcsncmp(spSrcNum.data() + cc, wsGroupSymbol.c_str(), iGroupLen) ==
1532                   0) {
1533             cc += iGroupLen;
1534           }
1535           break;
1536         }
1537         case '(':
1538         case ')':
1539           if (spSrcNum[cc] == spNumFormat[ccf])
1540             bNeg = true;
1541           else if (spSrcNum[cc] != L' ')
1542             return false;
1543 
1544           cc++;
1545           break;
1546         default:
1547           if (spNumFormat[ccf] != spSrcNum[cc])
1548             return false;
1549 
1550           cc++;
1551       }
1552     }
1553     if (cc != spSrcNum.size())
1554       return false;
1555   }
1556   if (iExponent || bHavePercentSymbol) {
1557     CFGAS_Decimal decimal = CFGAS_Decimal(wsValue->AsStringView());
1558     if (iExponent)
1559       decimal = decimal * CFGAS_Decimal(powf(10, iExponent), 3);
1560     if (bHavePercentSymbol)
1561       decimal = decimal / CFGAS_Decimal(100);
1562     *wsValue = decimal.ToWideString();
1563   }
1564   if (bNeg)
1565     wsValue->InsertAtFront(L'-');
1566 
1567   return true;
1568 }
1569 
GetDateTimeFormat(LocaleMgrIface * pLocaleMgr,LocaleIface ** pLocale,WideString * wsDatePattern,WideString * wsTimePattern) const1570 CFGAS_StringFormatter::DateTimeType CFGAS_StringFormatter::GetDateTimeFormat(
1571     LocaleMgrIface* pLocaleMgr,
1572     LocaleIface** pLocale,
1573     WideString* wsDatePattern,
1574     WideString* wsTimePattern) const {
1575   *pLocale = nullptr;
1576   WideString wsTempPattern;
1577   Category eCategory = Category::kUnknown;
1578   DateTimeType eDateTimeType = DateTimeType::kUnknown;
1579   size_t ccf = 0;
1580   bool bBraceOpen = false;
1581   while (ccf < m_spPattern.size()) {
1582     if (m_spPattern[ccf] == '\'') {
1583       size_t iCurChar = ccf;
1584       GetLiteralText(m_spPattern, &ccf);
1585       wsTempPattern +=
1586           WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
1587     } else if (!bBraceOpen && eDateTimeType != DateTimeType::kDateTime &&
1588                !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
1589       WideString wsCategory(m_spPattern[ccf]);
1590       ccf++;
1591       while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
1592              m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
1593         if (m_spPattern[ccf] == 'T') {
1594           *wsDatePattern = m_wsPattern.First(ccf);
1595           *wsTimePattern = m_wsPattern.Last(m_wsPattern.GetLength() - ccf);
1596           wsTimePattern->SetAt(0, ' ');
1597           if (!*pLocale)
1598             *pLocale = pLocaleMgr->GetDefLocale();
1599           return DateTimeType::kDateTime;
1600         }
1601         wsCategory += m_spPattern[ccf];
1602         ccf++;
1603       }
1604       if (!HasDate(eDateTimeType) && wsCategory.EqualsASCII("date")) {
1605         eDateTimeType = AddDateToDatelessType(eDateTimeType);
1606         eCategory = Category::kDate;
1607       } else if (!HasTime(eDateTimeType) && wsCategory.EqualsASCII("time")) {
1608         eDateTimeType = AddTimeToTimelessType(eDateTimeType);
1609         eCategory = Category::kTime;
1610       } else if (wsCategory.EqualsASCII("datetime")) {
1611         eDateTimeType = DateTimeType::kDateTime;
1612         eCategory = Category::kDateTime;
1613       } else {
1614         continue;
1615       }
1616       while (ccf < m_spPattern.size()) {
1617         if (m_spPattern[ccf] == '{') {
1618           bBraceOpen = true;
1619           break;
1620         }
1621         if (m_spPattern[ccf] == '(') {
1622           ccf++;
1623           WideString wsLCID;
1624           while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
1625             wsLCID += m_spPattern[ccf++];
1626 
1627           *pLocale = pLocaleMgr->GetLocaleByName(wsLCID);
1628         } else if (m_spPattern[ccf] == '.') {
1629           WideString wsSubCategory;
1630           ccf++;
1631           while (ccf < m_spPattern.size() && m_spPattern[ccf] != '(' &&
1632                  m_spPattern[ccf] != '{')
1633             wsSubCategory += m_spPattern[ccf++];
1634 
1635           uint32_t dwSubHash = FX_HashCode_GetW(wsSubCategory.AsStringView());
1636           LocaleIface::DateTimeSubcategory eSubCategory =
1637               LocaleIface::DateTimeSubcategory::kMedium;
1638           for (const auto& data : kLocaleDateTimeSubcategoryData) {
1639             if (data.uHash == dwSubHash) {
1640               eSubCategory = data.eSubCategory;
1641               break;
1642             }
1643           }
1644           if (!*pLocale)
1645             *pLocale = pLocaleMgr->GetDefLocale();
1646 
1647           switch (eCategory) {
1648             case Category::kDate:
1649               *wsDatePattern =
1650                   wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
1651               break;
1652             case Category::kTime:
1653               *wsTimePattern =
1654                   wsTempPattern + (*pLocale)->GetTimePattern(eSubCategory);
1655               break;
1656             case Category::kDateTime:
1657               *wsDatePattern =
1658                   wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
1659               *wsTimePattern = (*pLocale)->GetTimePattern(eSubCategory);
1660               break;
1661             default:
1662               break;
1663           }
1664           wsTempPattern.clear();
1665           continue;
1666         }
1667         ccf++;
1668       }
1669     } else if (m_spPattern[ccf] == '}') {
1670       bBraceOpen = false;
1671       if (!wsTempPattern.IsEmpty()) {
1672         if (eCategory == Category::kTime)
1673           *wsTimePattern = std::move(wsTempPattern);
1674         else if (eCategory == Category::kDate)
1675           *wsDatePattern = std::move(wsTempPattern);
1676         else
1677           wsTempPattern.clear();
1678       }
1679     } else {
1680       wsTempPattern += m_spPattern[ccf];
1681     }
1682     ccf++;
1683   }
1684 
1685   if (!wsTempPattern.IsEmpty()) {
1686     if (eCategory == Category::kDate)
1687       *wsDatePattern += wsTempPattern;
1688     else
1689       *wsTimePattern += wsTempPattern;
1690   }
1691   if (!*pLocale)
1692     *pLocale = pLocaleMgr->GetDefLocale();
1693   if (eDateTimeType == DateTimeType::kUnknown) {
1694     wsTimePattern->clear();
1695     *wsDatePattern = m_wsPattern;
1696   }
1697   return eDateTimeType;
1698 }
1699 
ParseDateTime(LocaleMgrIface * pLocaleMgr,const WideString & wsSrcDateTime,DateTimeType eDateTimeType,CFX_DateTime * dtValue) const1700 bool CFGAS_StringFormatter::ParseDateTime(LocaleMgrIface* pLocaleMgr,
1701                                           const WideString& wsSrcDateTime,
1702                                           DateTimeType eDateTimeType,
1703                                           CFX_DateTime* dtValue) const {
1704   dtValue->Reset();
1705   if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
1706     return false;
1707 
1708   LocaleIface* pLocale = nullptr;
1709   WideString wsDatePattern;
1710   WideString wsTimePattern;
1711   DateTimeType eCategory =
1712       GetDateTimeFormat(pLocaleMgr, &pLocale, &wsDatePattern, &wsTimePattern);
1713   if (!pLocale)
1714     return false;
1715 
1716   if (eCategory == DateTimeType::kUnknown)
1717     eCategory = eDateTimeType;
1718 
1719   size_t iStart = 0;
1720   switch (eCategory) {
1721     case DateTimeType::kDate:
1722       return ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1723                              &iStart);
1724     case DateTimeType::kTime:
1725       return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1726                              &iStart);
1727     case DateTimeType::kDateTime:
1728       return ParseLocaleDate(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1729                              &iStart) &&
1730              ParseLocaleTime(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1731                              &iStart);
1732     case DateTimeType::kTimeDate:
1733       return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1734                              &iStart) &&
1735              ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1736                              &iStart);
1737     case DateTimeType::kUnknown:
1738     default:
1739       return false;
1740   }
1741 }
1742 
ParseZero(const WideString & wsSrcText) const1743 bool CFGAS_StringFormatter::ParseZero(const WideString& wsSrcText) const {
1744   WideString wsTextFormat = GetTextFormat(L"zero");
1745   pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1746   pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1747 
1748   size_t iText = 0;
1749   size_t iPattern = 0;
1750   while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
1751     if (spTextFormat[iPattern] == '\'') {
1752       WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
1753       size_t iLiteralLen = wsLiteral.GetLength();
1754       if (iText + iLiteralLen > spSrcText.size() ||
1755           wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
1756         return false;
1757       }
1758       iText += iLiteralLen;
1759       iPattern++;
1760       continue;
1761     }
1762     if (spTextFormat[iPattern] != spSrcText[iText])
1763       return false;
1764 
1765     iText++;
1766     iPattern++;
1767   }
1768   return iPattern == spTextFormat.size() && iText == spSrcText.size();
1769 }
1770 
ParseNull(const WideString & wsSrcText) const1771 bool CFGAS_StringFormatter::ParseNull(const WideString& wsSrcText) const {
1772   WideString wsTextFormat = GetTextFormat(L"null");
1773   pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1774   pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1775 
1776   size_t iText = 0;
1777   size_t iPattern = 0;
1778   while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
1779     if (spTextFormat[iPattern] == '\'') {
1780       WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
1781       size_t iLiteralLen = wsLiteral.GetLength();
1782       if (iText + iLiteralLen > spSrcText.size() ||
1783           wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
1784         return false;
1785       }
1786       iText += iLiteralLen;
1787       iPattern++;
1788       continue;
1789     }
1790     if (spTextFormat[iPattern] != spSrcText[iText])
1791       return false;
1792 
1793     iText++;
1794     iPattern++;
1795   }
1796   return iPattern == spTextFormat.size() && iText == spSrcText.size();
1797 }
1798 
FormatText(const WideString & wsSrcText,WideString * wsOutput) const1799 bool CFGAS_StringFormatter::FormatText(const WideString& wsSrcText,
1800                                        WideString* wsOutput) const {
1801   if (wsSrcText.IsEmpty() || m_spPattern.empty())
1802     return false;
1803 
1804   WideString wsTextFormat = GetTextFormat(L"text");
1805   pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1806   pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1807 
1808   size_t iText = 0;
1809   size_t iPattern = 0;
1810   while (iPattern < spTextFormat.size()) {
1811     switch (spTextFormat[iPattern]) {
1812       case '\'': {
1813         *wsOutput += GetLiteralText(spTextFormat, &iPattern);
1814         iPattern++;
1815         break;
1816       }
1817       case 'A':
1818         if (iText >= spSrcText.size() || !FXSYS_iswalpha(spSrcText[iText]))
1819           return false;
1820 
1821         *wsOutput += spSrcText[iText++];
1822         iPattern++;
1823         break;
1824       case 'X':
1825         if (iText >= spSrcText.size())
1826           return false;
1827 
1828         *wsOutput += spSrcText[iText++];
1829         iPattern++;
1830         break;
1831       case 'O':
1832       case '0':
1833         if (iText >= spSrcText.size() ||
1834             (!FXSYS_IsDecimalDigit(spSrcText[iText]) &&
1835              !FXSYS_iswalpha(spSrcText[iText]))) {
1836           return false;
1837         }
1838         *wsOutput += spSrcText[iText++];
1839         iPattern++;
1840         break;
1841       case '9':
1842         if (iText >= spSrcText.size() ||
1843             !FXSYS_IsDecimalDigit(spSrcText[iText]))
1844           return false;
1845 
1846         *wsOutput += spSrcText[iText++];
1847         iPattern++;
1848         break;
1849       default:
1850         *wsOutput += spTextFormat[iPattern++];
1851         break;
1852     }
1853   }
1854   return iText == spSrcText.size();
1855 }
1856 
FormatNum(LocaleMgrIface * pLocaleMgr,const WideString & wsInputNum,WideString * wsOutput) const1857 bool CFGAS_StringFormatter::FormatNum(LocaleMgrIface* pLocaleMgr,
1858                                       const WideString& wsInputNum,
1859                                       WideString* wsOutput) const {
1860   if (wsInputNum.IsEmpty() || m_spPattern.empty())
1861     return false;
1862 
1863   size_t dot_index_f = m_spPattern.size();
1864   uint32_t dwNumStyle = 0;
1865   WideString wsNumFormat;
1866   LocaleIface* pLocale =
1867       GetNumericFormat(pLocaleMgr, &dot_index_f, &dwNumStyle, &wsNumFormat);
1868   if (!pLocale || wsNumFormat.IsEmpty())
1869     return false;
1870 
1871   pdfium::span<const wchar_t> spNumFormat = wsNumFormat.span();
1872   WideString wsSrcNum = wsInputNum;
1873   wsSrcNum.TrimLeft('0');
1874   if (wsSrcNum.IsEmpty() || wsSrcNum[0] == '.')
1875     wsSrcNum.InsertAtFront('0');
1876 
1877   CFGAS_Decimal decimal = CFGAS_Decimal(wsSrcNum.AsStringView());
1878   if (dwNumStyle & FX_NUMSTYLE_Percent) {
1879     decimal = decimal * CFGAS_Decimal(100);
1880     wsSrcNum = decimal.ToWideString();
1881   }
1882 
1883   int32_t exponent = 0;
1884   if (dwNumStyle & FX_NUMSTYLE_Exponent) {
1885     int fixed_count = 0;
1886     for (size_t ccf = 0; ccf < dot_index_f; ++ccf) {
1887       switch (spNumFormat[ccf]) {
1888         case '\'':
1889           GetLiteralText(spNumFormat, &ccf);
1890           break;
1891         case '9':
1892         case 'z':
1893         case 'Z':
1894           fixed_count++;
1895           break;
1896       }
1897     }
1898 
1899     FX_SAFE_UINT32 threshold = 1;
1900     while (fixed_count > 1) {
1901       threshold *= 10;
1902       fixed_count--;
1903     }
1904     if (!threshold.IsValid())
1905       return false;
1906 
1907     bool bAdjusted = false;
1908     while (decimal.IsNotZero() &&
1909            fabs(decimal.ToDouble()) < threshold.ValueOrDie()) {
1910       decimal = decimal * CFGAS_Decimal(10);
1911       --exponent;
1912       bAdjusted = true;
1913     }
1914     if (!bAdjusted) {
1915       threshold *= 10;
1916       if (!threshold.IsValid())
1917         return false;
1918 
1919       while (decimal.IsNotZero() &&
1920              fabs(decimal.ToDouble()) > threshold.ValueOrDie()) {
1921         decimal = decimal / CFGAS_Decimal(10);
1922         ++exponent;
1923       }
1924     }
1925   }
1926 
1927   bool bTrimTailZeros = false;
1928   size_t iTreading =
1929       GetNumTrailingLimit(wsNumFormat, dot_index_f, &bTrimTailZeros);
1930   uint8_t scale = decimal.GetScale();
1931   if (iTreading < scale) {
1932     decimal.SetScale(iTreading);
1933     wsSrcNum = decimal.ToWideString();
1934   }
1935   if (bTrimTailZeros && scale > 0 && iTreading > 0) {
1936     wsSrcNum.TrimRight(L"0");
1937     wsSrcNum.TrimRight(L".");
1938   }
1939 
1940   WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
1941   bool bNeg = false;
1942   if (wsSrcNum[0] == '-') {
1943     bNeg = true;
1944     wsSrcNum.Delete(0, 1);
1945   }
1946 
1947   bool bAddNeg = false;
1948   pdfium::span<const wchar_t> spSrcNum = wsSrcNum.span();
1949   auto dot_index = wsSrcNum.Find('.');
1950   if (!dot_index.has_value())
1951     dot_index = spSrcNum.size();
1952 
1953   size_t cc = dot_index.value() - 1;
1954   for (size_t ccf = dot_index_f - 1; ccf < spNumFormat.size(); --ccf) {
1955     switch (spNumFormat[ccf]) {
1956       case '9':
1957         if (cc < spSrcNum.size()) {
1958           if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1959             return false;
1960           wsOutput->InsertAtFront(spSrcNum[cc]);
1961           cc--;
1962         } else {
1963           wsOutput->InsertAtFront(L'0');
1964         }
1965         break;
1966       case 'z':
1967         if (cc < spSrcNum.size()) {
1968           if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1969             return false;
1970           if (spSrcNum[0] != '0')
1971             wsOutput->InsertAtFront(spSrcNum[cc]);
1972           cc--;
1973         }
1974         break;
1975       case 'Z':
1976         if (cc < spSrcNum.size()) {
1977           if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1978             return false;
1979           wsOutput->InsertAtFront(spSrcNum[0] == '0' ? L' ' : spSrcNum[cc]);
1980           cc--;
1981         } else {
1982           wsOutput->InsertAtFront(L' ');
1983         }
1984         break;
1985       case 'S':
1986         if (bNeg) {
1987           *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
1988           bAddNeg = true;
1989         } else {
1990           wsOutput->InsertAtFront(L' ');
1991         }
1992         break;
1993       case 's':
1994         if (bNeg) {
1995           *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
1996           bAddNeg = true;
1997         }
1998         break;
1999       case 'E':
2000         *wsOutput = WideString::Format(L"E%+d", exponent) + *wsOutput;
2001         break;
2002       case '$':
2003         *wsOutput = pLocale->GetCurrencySymbol() + *wsOutput;
2004         break;
2005       case 'r':
2006         if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'c') {
2007           if (bNeg)
2008             *wsOutput = L"CR" + *wsOutput;
2009           ccf--;
2010           bAddNeg = true;
2011         } else {
2012           wsOutput->InsertAtFront('r');
2013         }
2014         break;
2015       case 'R':
2016         if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'C') {
2017           *wsOutput = bNeg ? L"CR" : L"  " + *wsOutput;
2018           ccf--;
2019           bAddNeg = true;
2020         } else {
2021           wsOutput->InsertAtFront('R');
2022         }
2023         break;
2024       case 'b':
2025         if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'd') {
2026           if (bNeg)
2027             *wsOutput = L"db" + *wsOutput;
2028           ccf--;
2029           bAddNeg = true;
2030         } else {
2031           wsOutput->InsertAtFront('b');
2032         }
2033         break;
2034       case 'B':
2035         if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'D') {
2036           *wsOutput = bNeg ? L"DB" : L"  " + *wsOutput;
2037           ccf--;
2038           bAddNeg = true;
2039         } else {
2040           wsOutput->InsertAtFront('B');
2041         }
2042         break;
2043       case '%':
2044         *wsOutput = pLocale->GetPercentSymbol() + *wsOutput;
2045         break;
2046       case ',':
2047         if (cc < spSrcNum.size())
2048           *wsOutput = wsGroupSymbol + *wsOutput;
2049         break;
2050       case '(':
2051         wsOutput->InsertAtFront(bNeg ? L'(' : L' ');
2052         bAddNeg = true;
2053         break;
2054       case ')':
2055         wsOutput->InsertAtFront(bNeg ? L')' : L' ');
2056         break;
2057       case '\'':
2058         *wsOutput = GetLiteralTextReverse(spNumFormat, &ccf) + *wsOutput;
2059         break;
2060       default:
2061         wsOutput->InsertAtFront(spNumFormat[ccf]);
2062         break;
2063     }
2064   }
2065 
2066   if (cc < spSrcNum.size()) {
2067     size_t nPos = dot_index.value() % 3;
2068     wsOutput->clear();
2069     for (size_t i = 0; i < dot_index.value(); i++) {
2070       if (i % 3 == nPos && i != 0)
2071         *wsOutput += wsGroupSymbol;
2072       *wsOutput += wsSrcNum[i];
2073     }
2074     if (dot_index.value() < spSrcNum.size()) {
2075       *wsOutput += pLocale->GetDecimalSymbol();
2076       *wsOutput += wsSrcNum.Last(spSrcNum.size() - dot_index.value() - 1);
2077     }
2078     if (bNeg)
2079       *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
2080     return true;
2081   }
2082   if (dot_index_f == wsNumFormat.GetLength()) {
2083     if (!bAddNeg && bNeg)
2084       *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
2085     return true;
2086   }
2087 
2088   WideString wsDotSymbol = pLocale->GetDecimalSymbol();
2089   if (spNumFormat[dot_index_f] == 'V') {
2090     *wsOutput += wsDotSymbol;
2091   } else if (spNumFormat[dot_index_f] == '.') {
2092     if (dot_index.value() < spSrcNum.size()) {
2093       *wsOutput += wsDotSymbol;
2094     } else if (dot_index_f + 1 < spNumFormat.size() &&
2095                (spNumFormat[dot_index_f + 1] == '9' ||
2096                 spNumFormat[dot_index_f + 1] == 'Z')) {
2097       *wsOutput += wsDotSymbol;
2098     }
2099   }
2100 
2101   cc = dot_index.value() + 1;
2102   for (size_t ccf = dot_index_f + 1; ccf < spNumFormat.size(); ++ccf) {
2103     switch (spNumFormat[ccf]) {
2104       case '\'':
2105         *wsOutput += GetLiteralText(spNumFormat, &ccf);
2106         break;
2107       case '9':
2108         if (cc < spSrcNum.size()) {
2109           if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
2110             return false;
2111           *wsOutput += spSrcNum[cc];
2112           cc++;
2113         } else {
2114           *wsOutput += L'0';
2115         }
2116         break;
2117       case 'z':
2118         if (cc < spSrcNum.size()) {
2119           if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
2120             return false;
2121           *wsOutput += spSrcNum[cc];
2122           cc++;
2123         }
2124         break;
2125       case 'Z':
2126         if (cc < spSrcNum.size()) {
2127           if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
2128             return false;
2129           *wsOutput += spSrcNum[cc];
2130           cc++;
2131         } else {
2132           *wsOutput += L'0';
2133         }
2134         break;
2135       case 'E': {
2136         *wsOutput += WideString::Format(L"E%+d", exponent);
2137         break;
2138       }
2139       case '$':
2140         *wsOutput += pLocale->GetCurrencySymbol();
2141         break;
2142       case 'c':
2143         if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'r') {
2144           if (bNeg)
2145             *wsOutput += L"CR";
2146           ccf++;
2147           bAddNeg = true;
2148         }
2149         break;
2150       case 'C':
2151         if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'R') {
2152           *wsOutput += bNeg ? L"CR" : L"  ";
2153           ccf++;
2154           bAddNeg = true;
2155         }
2156         break;
2157       case 'd':
2158         if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'b') {
2159           if (bNeg)
2160             *wsOutput += L"db";
2161           ccf++;
2162           bAddNeg = true;
2163         }
2164         break;
2165       case 'D':
2166         if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'B') {
2167           *wsOutput += bNeg ? L"DB" : L"  ";
2168           ccf++;
2169           bAddNeg = true;
2170         }
2171         break;
2172       case '%':
2173         *wsOutput += pLocale->GetPercentSymbol();
2174         break;
2175       case '8':
2176         while (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == '8')
2177           ccf++;
2178         while (cc < spSrcNum.size() && FXSYS_IsDecimalDigit(spSrcNum[cc])) {
2179           *wsOutput += spSrcNum[cc];
2180           cc++;
2181         }
2182         break;
2183       case ',':
2184         *wsOutput += wsGroupSymbol;
2185         break;
2186       case '(':
2187         *wsOutput += bNeg ? '(' : ' ';
2188         bAddNeg = true;
2189         break;
2190       case ')':
2191         *wsOutput += bNeg ? ')' : ' ';
2192         break;
2193       default:
2194         break;
2195     }
2196   }
2197   if (!bAddNeg && bNeg)
2198     *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
2199 
2200   return true;
2201 }
2202 
FormatDateTime(LocaleMgrIface * pLocaleMgr,const WideString & wsSrcDateTime,DateTimeType eDateTimeType,WideString * wsOutput) const2203 bool CFGAS_StringFormatter::FormatDateTime(LocaleMgrIface* pLocaleMgr,
2204                                            const WideString& wsSrcDateTime,
2205                                            DateTimeType eDateTimeType,
2206                                            WideString* wsOutput) const {
2207   if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
2208     return false;
2209 
2210   WideString wsDatePattern;
2211   WideString wsTimePattern;
2212   LocaleIface* pLocale = nullptr;
2213   DateTimeType eCategory =
2214       GetDateTimeFormat(pLocaleMgr, &pLocale, &wsDatePattern, &wsTimePattern);
2215   if (!pLocale)
2216     return false;
2217 
2218   if (eCategory == DateTimeType::kUnknown) {
2219     if (eDateTimeType == DateTimeType::kTime) {
2220       wsTimePattern = std::move(wsDatePattern);
2221       wsDatePattern = WideString();
2222     }
2223     eCategory = eDateTimeType;
2224     if (eCategory == DateTimeType::kUnknown)
2225       return false;
2226   }
2227 
2228   CFX_DateTime dt;
2229   auto iT = wsSrcDateTime.Find(L"T");
2230   if (!iT.has_value()) {
2231     if (eCategory == DateTimeType::kDate &&
2232         FX_DateFromCanonical(wsSrcDateTime.span(), &dt)) {
2233       *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
2234                                          pLocale);
2235       return true;
2236     }
2237     if (eCategory == DateTimeType::kTime &&
2238         FX_TimeFromCanonical(pLocale, wsSrcDateTime.span(), &dt)) {
2239       *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
2240                                          pLocale);
2241       return true;
2242     }
2243   } else {
2244     pdfium::span<const wchar_t> wsSrcDate =
2245         wsSrcDateTime.span().first(iT.value());
2246     pdfium::span<const wchar_t> wsSrcTime =
2247         wsSrcDateTime.span().subspan(iT.value() + 1);
2248     if (wsSrcDate.empty() || wsSrcTime.empty())
2249       return false;
2250 
2251     if (FX_DateFromCanonical(wsSrcDate, &dt) &&
2252         FX_TimeFromCanonical(pLocale, wsSrcTime, &dt)) {
2253       *wsOutput =
2254           FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern,
2255                                  eCategory != DateTimeType::kTimeDate, pLocale);
2256       return true;
2257     }
2258   }
2259   return false;
2260 }
2261 
FormatZero(WideString * wsOutput) const2262 bool CFGAS_StringFormatter::FormatZero(WideString* wsOutput) const {
2263   if (m_spPattern.empty())
2264     return false;
2265 
2266   WideString wsTextFormat = GetTextFormat(L"zero");
2267   pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
2268   for (size_t iPattern = 0; iPattern < spTextFormat.size(); ++iPattern) {
2269     if (spTextFormat[iPattern] == '\'') {
2270       *wsOutput += GetLiteralText(spTextFormat, &iPattern);
2271       continue;
2272     }
2273     *wsOutput += spTextFormat[iPattern];
2274   }
2275   return true;
2276 }
2277 
FormatNull(WideString * wsOutput) const2278 bool CFGAS_StringFormatter::FormatNull(WideString* wsOutput) const {
2279   if (m_spPattern.empty())
2280     return false;
2281 
2282   WideString wsTextFormat = GetTextFormat(L"null");
2283   pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
2284   for (size_t iPattern = 0; iPattern < spTextFormat.size(); ++iPattern) {
2285     if (spTextFormat[iPattern] == '\'') {
2286       *wsOutput += GetLiteralText(spTextFormat, &iPattern);
2287       continue;
2288     }
2289     *wsOutput += spTextFormat[iPattern];
2290   }
2291   return true;
2292 }
2293