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