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/fxfa/parser/cxfa_localevalue.h"
8 
9 #include <cwchar>
10 #include <utility>
11 #include <vector>
12 
13 #include "core/fxcrt/fx_extension.h"
14 #include "third_party/base/ptr_util.h"
15 #include "third_party/base/span.h"
16 #include "third_party/base/stl_util.h"
17 #include "xfa/fgas/crt/cfgas_stringformatter.h"
18 #include "xfa/fxfa/parser/cxfa_document.h"
19 #include "xfa/fxfa/parser/cxfa_localemgr.h"
20 #include "xfa/fxfa/parser/xfa_utils.h"
21 
22 namespace {
23 
ValueCategory(FX_LOCALECATEGORY eCategory,uint32_t dwValueType)24 FX_LOCALECATEGORY ValueCategory(FX_LOCALECATEGORY eCategory,
25                                 uint32_t dwValueType) {
26   if (eCategory != FX_LOCALECATEGORY_Unknown)
27     return eCategory;
28 
29   switch (dwValueType) {
30     case XFA_VT_BOOLEAN:
31     case XFA_VT_INTEGER:
32     case XFA_VT_DECIMAL:
33     case XFA_VT_FLOAT:
34       return FX_LOCALECATEGORY_Num;
35     case XFA_VT_TEXT:
36       return FX_LOCALECATEGORY_Text;
37     case XFA_VT_DATE:
38       return FX_LOCALECATEGORY_Date;
39     case XFA_VT_TIME:
40       return FX_LOCALECATEGORY_Time;
41     case XFA_VT_DATETIME:
42       return FX_LOCALECATEGORY_DateTime;
43   }
44   return FX_LOCALECATEGORY_Unknown;
45 }
46 
ValueSplitDateTime(const WideString & wsDateTime,WideString & wsDate,WideString & wsTime)47 bool ValueSplitDateTime(const WideString& wsDateTime,
48                         WideString& wsDate,
49                         WideString& wsTime) {
50   wsDate.clear();
51   wsTime.clear();
52   if (wsDateTime.IsEmpty())
53     return false;
54 
55   auto nSplitIndex = wsDateTime.Find('T');
56   if (!nSplitIndex.has_value())
57     nSplitIndex = wsDateTime.Find(' ');
58   if (!nSplitIndex.has_value())
59     return false;
60 
61   wsDate = wsDateTime.First(nSplitIndex.value());
62   wsTime = wsDateTime.Last(wsDateTime.GetLength() - nSplitIndex.value() - 1);
63   return true;
64 }
65 
66 class ScopedLocale {
67  public:
ScopedLocale(CXFA_LocaleMgr * pLocaleMgr,LocaleIface * pNewLocale)68   ScopedLocale(CXFA_LocaleMgr* pLocaleMgr, LocaleIface* pNewLocale)
69       : m_pLocaleMgr(pLocaleMgr),
70         m_pNewLocale(pNewLocale),
71         m_pOrigLocale(pNewLocale ? m_pLocaleMgr->GetDefLocale() : nullptr) {
72     if (m_pNewLocale)
73       m_pLocaleMgr->SetDefLocale(pNewLocale);
74   }
75 
~ScopedLocale()76   ~ScopedLocale() {
77     if (m_pNewLocale)
78       m_pLocaleMgr->SetDefLocale(m_pOrigLocale);
79   }
80 
81   ScopedLocale(const ScopedLocale& that) = delete;
82   ScopedLocale& operator=(const ScopedLocale& that) = delete;
83 
84  private:
85   UnownedPtr<CXFA_LocaleMgr> const m_pLocaleMgr;
86   LocaleIface* const m_pNewLocale;
87   LocaleIface* const m_pOrigLocale;
88 };
89 
90 }  // namespace
91 
92 CXFA_LocaleValue::CXFA_LocaleValue() = default;
93 
CXFA_LocaleValue(uint32_t dwType,CXFA_LocaleMgr * pLocaleMgr)94 CXFA_LocaleValue::CXFA_LocaleValue(uint32_t dwType, CXFA_LocaleMgr* pLocaleMgr)
95     : m_pLocaleMgr(pLocaleMgr),
96       m_dwType(dwType),
97       m_bValid(m_dwType != XFA_VT_NULL) {}
98 
CXFA_LocaleValue(uint32_t dwType,const WideString & wsValue,CXFA_LocaleMgr * pLocaleMgr)99 CXFA_LocaleValue::CXFA_LocaleValue(uint32_t dwType,
100                                    const WideString& wsValue,
101                                    CXFA_LocaleMgr* pLocaleMgr)
102     : m_pLocaleMgr(pLocaleMgr),
103       m_wsValue(wsValue),
104       m_dwType(dwType),
105       m_bValid(ValidateCanonicalValue(wsValue, dwType)) {}
106 
CXFA_LocaleValue(uint32_t dwType,const WideString & wsValue,const WideString & wsFormat,LocaleIface * pLocale,CXFA_LocaleMgr * pLocaleMgr)107 CXFA_LocaleValue::CXFA_LocaleValue(uint32_t dwType,
108                                    const WideString& wsValue,
109                                    const WideString& wsFormat,
110                                    LocaleIface* pLocale,
111                                    CXFA_LocaleMgr* pLocaleMgr)
112     : m_pLocaleMgr(pLocaleMgr),
113       m_dwType(dwType),
114       m_bValid(ParsePatternValue(wsValue, wsFormat, pLocale)) {}
115 
116 CXFA_LocaleValue::CXFA_LocaleValue(const CXFA_LocaleValue& that) = default;
117 
118 CXFA_LocaleValue& CXFA_LocaleValue::operator=(const CXFA_LocaleValue& that) =
119     default;
120 
121 CXFA_LocaleValue::~CXFA_LocaleValue() = default;
122 
ValidateValue(const WideString & wsValue,const WideString & wsPattern,LocaleIface * pLocale,WideString * pMatchFormat)123 bool CXFA_LocaleValue::ValidateValue(const WideString& wsValue,
124                                      const WideString& wsPattern,
125                                      LocaleIface* pLocale,
126                                      WideString* pMatchFormat) {
127   if (!m_pLocaleMgr)
128     return false;
129 
130   ScopedLocale scoped_locale(m_pLocaleMgr.Get(), pLocale);
131   std::vector<WideString> wsPatterns =
132       CFGAS_StringFormatter::SplitOnBars(wsPattern);
133 
134   WideString wsOutput;
135   bool bRet = false;
136   size_t i = 0;
137   for (; !bRet && i < wsPatterns.size(); i++) {
138     const WideString& wsFormat = wsPatterns[i];
139     auto pFormat =
140         pdfium::MakeUnique<CFGAS_StringFormatter>(m_pLocaleMgr.Get(), wsFormat);
141     switch (ValueCategory(pFormat->GetCategory(), m_dwType)) {
142       case FX_LOCALECATEGORY_Null:
143         bRet = pFormat->ParseNull(wsValue);
144         if (!bRet)
145           bRet = wsValue.IsEmpty();
146         break;
147       case FX_LOCALECATEGORY_Zero:
148         bRet = pFormat->ParseZero(wsValue);
149         if (!bRet)
150           bRet = wsValue.EqualsASCII("0");
151         break;
152       case FX_LOCALECATEGORY_Num: {
153         WideString fNum;
154         bRet = pFormat->ParseNum(wsValue, &fNum);
155         if (!bRet)
156           bRet = pFormat->FormatNum(wsValue, &wsOutput);
157         break;
158       }
159       case FX_LOCALECATEGORY_Text:
160         bRet = pFormat->ParseText(wsValue, &wsOutput);
161         wsOutput.clear();
162         if (!bRet)
163           bRet = pFormat->FormatText(wsValue, &wsOutput);
164         break;
165       case FX_LOCALECATEGORY_Date: {
166         CFX_DateTime dt;
167         bRet = ValidateCanonicalDate(wsValue, &dt);
168         if (!bRet) {
169           bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Date, &dt);
170           if (!bRet) {
171             bRet = pFormat->FormatDateTime(wsValue, FX_DATETIMETYPE_Date,
172                                            &wsOutput);
173           }
174         }
175         break;
176       }
177       case FX_LOCALECATEGORY_Time: {
178         CFX_DateTime dt;
179         bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Time, &dt);
180         if (!bRet) {
181           bRet =
182               pFormat->FormatDateTime(wsValue, FX_DATETIMETYPE_Time, &wsOutput);
183         }
184         break;
185       }
186       case FX_LOCALECATEGORY_DateTime: {
187         CFX_DateTime dt;
188         bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_DateTime, &dt);
189         if (!bRet) {
190           bRet = pFormat->FormatDateTime(wsValue, FX_DATETIMETYPE_DateTime,
191                                          &wsOutput);
192         }
193         break;
194       }
195       default:
196         bRet = false;
197         break;
198     }
199   }
200   if (bRet && pMatchFormat)
201     *pMatchFormat = wsPatterns[i - 1];
202   return bRet;
203 }
204 
GetDoubleNum() const205 double CXFA_LocaleValue::GetDoubleNum() const {
206   if (!m_bValid || (m_dwType != XFA_VT_BOOLEAN && m_dwType != XFA_VT_INTEGER &&
207                     m_dwType != XFA_VT_DECIMAL && m_dwType != XFA_VT_FLOAT)) {
208     return 0;
209   }
210 
211   return wcstod(m_wsValue.c_str(), nullptr);
212 }
213 
GetDate() const214 CFX_DateTime CXFA_LocaleValue::GetDate() const {
215   if (!m_bValid || m_dwType != XFA_VT_DATE)
216     return CFX_DateTime();
217 
218   CFX_DateTime dt;
219   FX_DateFromCanonical(m_wsValue.span(), &dt);
220   return dt;
221 }
222 
GetTime() const223 CFX_DateTime CXFA_LocaleValue::GetTime() const {
224   if (!m_bValid || m_dwType != XFA_VT_TIME)
225     return CFX_DateTime();
226 
227   CFX_DateTime dt;
228   FX_TimeFromCanonical(m_pLocaleMgr->GetDefLocale(), m_wsValue.span(), &dt);
229   return dt;
230 }
231 
SetDate(const CFX_DateTime & d)232 bool CXFA_LocaleValue::SetDate(const CFX_DateTime& d) {
233   m_dwType = XFA_VT_DATE;
234   m_wsValue = WideString::Format(L"%04d-%02d-%02d", d.GetYear(), d.GetMonth(),
235                                  d.GetDay());
236   return true;
237 }
238 
SetTime(const CFX_DateTime & t)239 bool CXFA_LocaleValue::SetTime(const CFX_DateTime& t) {
240   m_dwType = XFA_VT_TIME;
241   m_wsValue = WideString::Format(L"%02d:%02d:%02d", t.GetHour(), t.GetMinute(),
242                                  t.GetSecond());
243   if (t.GetMillisecond() > 0)
244     m_wsValue += WideString::Format(L"%:03d", t.GetMillisecond());
245   return true;
246 }
247 
SetDateTime(const CFX_DateTime & dt)248 bool CXFA_LocaleValue::SetDateTime(const CFX_DateTime& dt) {
249   m_dwType = XFA_VT_DATETIME;
250   m_wsValue = WideString::Format(L"%04d-%02d-%02dT%02d:%02d:%02d", dt.GetYear(),
251                                  dt.GetMonth(), dt.GetDay(), dt.GetHour(),
252                                  dt.GetMinute(), dt.GetSecond());
253   if (dt.GetMillisecond() > 0)
254     m_wsValue += WideString::Format(L"%:03d", dt.GetMillisecond());
255   return true;
256 }
257 
FormatPatterns(WideString & wsResult,const WideString & wsFormat,LocaleIface * pLocale,XFA_VALUEPICTURE eValueType) const258 bool CXFA_LocaleValue::FormatPatterns(WideString& wsResult,
259                                       const WideString& wsFormat,
260                                       LocaleIface* pLocale,
261                                       XFA_VALUEPICTURE eValueType) const {
262   wsResult.clear();
263   for (const auto& pattern : CFGAS_StringFormatter::SplitOnBars(wsFormat)) {
264     if (FormatSinglePattern(wsResult, pattern, pLocale, eValueType))
265       return true;
266   }
267   return false;
268 }
269 
FormatSinglePattern(WideString & wsResult,const WideString & wsFormat,LocaleIface * pLocale,XFA_VALUEPICTURE eValueType) const270 bool CXFA_LocaleValue::FormatSinglePattern(WideString& wsResult,
271                                            const WideString& wsFormat,
272                                            LocaleIface* pLocale,
273                                            XFA_VALUEPICTURE eValueType) const {
274   if (!m_pLocaleMgr)
275     return false;
276 
277   ScopedLocale scoped_locale(m_pLocaleMgr.Get(), pLocale);
278 
279   wsResult.clear();
280   bool bRet = false;
281   auto pFormat =
282       pdfium::MakeUnique<CFGAS_StringFormatter>(m_pLocaleMgr.Get(), wsFormat);
283   FX_LOCALECATEGORY eCategory = ValueCategory(pFormat->GetCategory(), m_dwType);
284   switch (eCategory) {
285     case FX_LOCALECATEGORY_Null:
286       if (m_wsValue.IsEmpty())
287         bRet = pFormat->FormatNull(&wsResult);
288       break;
289     case FX_LOCALECATEGORY_Zero:
290       if (m_wsValue.EqualsASCII("0"))
291         bRet = pFormat->FormatZero(&wsResult);
292       break;
293     case FX_LOCALECATEGORY_Num:
294       bRet = pFormat->FormatNum(m_wsValue, &wsResult);
295       break;
296     case FX_LOCALECATEGORY_Text:
297       bRet = pFormat->FormatText(m_wsValue, &wsResult);
298       break;
299     case FX_LOCALECATEGORY_Date:
300       bRet =
301           pFormat->FormatDateTime(m_wsValue, FX_DATETIMETYPE_Date, &wsResult);
302       break;
303     case FX_LOCALECATEGORY_Time:
304       bRet =
305           pFormat->FormatDateTime(m_wsValue, FX_DATETIMETYPE_Time, &wsResult);
306       break;
307     case FX_LOCALECATEGORY_DateTime:
308       bRet = pFormat->FormatDateTime(m_wsValue, FX_DATETIMETYPE_DateTime,
309                                      &wsResult);
310       break;
311     default:
312       wsResult = m_wsValue;
313       bRet = true;
314   }
315   if (!bRet && (eCategory != FX_LOCALECATEGORY_Num ||
316                 eValueType != XFA_VALUEPICTURE_Display)) {
317     wsResult = m_wsValue;
318   }
319 
320   return bRet;
321 }
322 
ValidateCanonicalValue(const WideString & wsValue,uint32_t dwVType)323 bool CXFA_LocaleValue::ValidateCanonicalValue(const WideString& wsValue,
324                                               uint32_t dwVType) {
325   if (wsValue.IsEmpty())
326     return true;
327 
328   CFX_DateTime dt;
329   switch (dwVType) {
330     case XFA_VT_DATE: {
331       if (ValidateCanonicalDate(wsValue, &dt))
332         return true;
333 
334       WideString wsDate;
335       WideString wsTime;
336       if (ValueSplitDateTime(wsValue, wsDate, wsTime) &&
337           ValidateCanonicalDate(wsDate, &dt)) {
338         return true;
339       }
340       return false;
341     }
342     case XFA_VT_TIME: {
343       if (ValidateCanonicalTime(wsValue))
344         return true;
345 
346       WideString wsDate;
347       WideString wsTime;
348       if (ValueSplitDateTime(wsValue, wsDate, wsTime) &&
349           ValidateCanonicalTime(wsTime)) {
350         return true;
351       }
352       return false;
353     }
354     case XFA_VT_DATETIME: {
355       WideString wsDate, wsTime;
356       if (ValueSplitDateTime(wsValue, wsDate, wsTime) &&
357           ValidateCanonicalDate(wsDate, &dt) && ValidateCanonicalTime(wsTime)) {
358         return true;
359       }
360     } break;
361   }
362   return true;
363 }
364 
ValidateCanonicalDate(const WideString & wsDate,CFX_DateTime * unDate)365 bool CXFA_LocaleValue::ValidateCanonicalDate(const WideString& wsDate,
366                                              CFX_DateTime* unDate) {
367   static const uint8_t LastDay[12] = {31, 28, 31, 30, 31, 30,
368                                       31, 31, 30, 31, 30, 31};
369   static const uint16_t wCountY = 4;
370   static const uint16_t wCountM = 2;
371   static const uint16_t wCountD = 2;
372   pdfium::span<const wchar_t> spDate = wsDate.span();
373   if (spDate.size() < wCountY ||
374       spDate.size() > wCountY + wCountM + wCountD + 2) {
375     return false;
376   }
377   const bool bSymbol = wsDate.Contains(0x2D);
378   uint16_t wYear = 0;
379   uint16_t wMonth = 0;
380   uint16_t wDay = 0;
381   size_t nIndex = 0;
382   size_t nStart = 0;
383   while (nIndex < wCountY && spDate[nIndex] != '\0') {
384     if (!FXSYS_IsDecimalDigit(spDate[nIndex]))
385       return false;
386 
387     wYear = (spDate[nIndex] - '0') + wYear * 10;
388     nIndex++;
389   }
390   if (bSymbol) {
391     if (nIndex >= spDate.size() || spDate[nIndex] != 0x2D)
392       return false;
393     nIndex++;
394   }
395 
396   nStart = nIndex;
397   while (nIndex < spDate.size() && spDate[nIndex] != '\0' &&
398          nIndex - nStart < wCountM) {
399     if (!FXSYS_IsDecimalDigit(spDate[nIndex]))
400       return false;
401 
402     wMonth = (spDate[nIndex] - '0') + wMonth * 10;
403     nIndex++;
404   }
405   if (bSymbol) {
406     if (nIndex >= spDate.size() || spDate[nIndex] != 0x2D)
407       return false;
408     nIndex++;
409   }
410 
411   nStart = nIndex;
412   while (nIndex < spDate.size() && spDate[nIndex] != '\0' &&
413          nIndex - nStart < wCountD) {
414     if (!FXSYS_IsDecimalDigit(spDate[nIndex]))
415       return false;
416 
417     wDay = (spDate[nIndex] - '0') + wDay * 10;
418     nIndex++;
419   }
420   if (nIndex != spDate.size())
421     return false;
422   if (wYear < 1900 || wYear > 2029)
423     return false;
424   if (wMonth < 1 || wMonth > 12)
425     return wMonth == 0 && spDate.size() == wCountY;
426   if (wDay < 1)
427     return wDay == 0 && spDate.size() == wCountY + wCountM;
428   if (wMonth == 2) {
429     if (wYear % 400 == 0 || (wYear % 100 != 0 && wYear % 4 == 0)) {
430       if (wDay > 29)
431         return false;
432     } else if (wDay > 28) {
433       return false;
434     }
435   } else if (wDay > LastDay[wMonth - 1]) {
436     return false;
437   }
438 
439   unDate->SetDate(wYear, static_cast<uint8_t>(wMonth),
440                   static_cast<uint8_t>(wDay));
441   return true;
442 }
443 
ValidateCanonicalTime(const WideString & wsTime)444 bool CXFA_LocaleValue::ValidateCanonicalTime(const WideString& wsTime) {
445   pdfium::span<const wchar_t> spTime = wsTime.span();
446   if (spTime.size() < 2)
447     return false;
448 
449   const uint16_t wCountH = 2;
450   const uint16_t wCountM = 2;
451   const uint16_t wCountS = 2;
452   const uint16_t wCountF = 3;
453   const bool bSymbol = wsTime.Contains(':');
454   uint16_t wHour = 0;
455   uint16_t wMinute = 0;
456   uint16_t wSecond = 0;
457   uint16_t wFraction = 0;
458   size_t nIndex = 0;
459   size_t nStart = 0;
460   while (nIndex - nStart < wCountH && spTime[nIndex]) {
461     if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
462       return false;
463     wHour = spTime[nIndex] - '0' + wHour * 10;
464     nIndex++;
465   }
466   if (bSymbol) {
467     if (nIndex < spTime.size() && spTime[nIndex] != ':')
468       return false;
469     nIndex++;
470   }
471 
472   nStart = nIndex;
473   while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
474          nIndex - nStart < wCountM) {
475     if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
476       return false;
477     wMinute = spTime[nIndex] - '0' + wMinute * 10;
478     nIndex++;
479   }
480   if (bSymbol) {
481     if (nIndex >= spTime.size() || spTime[nIndex] != ':')
482       return false;
483     nIndex++;
484   }
485   nStart = nIndex;
486   while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
487          nIndex - nStart < wCountS) {
488     if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
489       return false;
490     wSecond = spTime[nIndex] - '0' + wSecond * 10;
491     nIndex++;
492   }
493   auto pos = wsTime.Find('.');
494   if (pos.has_value() && pos.value() != 0) {
495     if (nIndex >= spTime.size() || spTime[nIndex] != '.')
496       return false;
497     nIndex++;
498     nStart = nIndex;
499     while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
500            nIndex - nStart < wCountF) {
501       if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
502         return false;
503       wFraction = spTime[nIndex] - '0' + wFraction * 10;
504       nIndex++;
505     }
506   }
507   if (nIndex < spTime.size()) {
508     if (spTime[nIndex] == 'Z') {
509       nIndex++;
510     } else if (spTime[nIndex] == '-' || spTime[nIndex] == '+') {
511       int16_t nOffsetH = 0;
512       int16_t nOffsetM = 0;
513       nIndex++;
514       nStart = nIndex;
515       while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
516              nIndex - nStart < wCountH) {
517         if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
518           return false;
519         nOffsetH = spTime[nIndex] - '0' + nOffsetH * 10;
520         nIndex++;
521       }
522       if (bSymbol) {
523         if (nIndex >= spTime.size() || spTime[nIndex] != ':')
524           return false;
525         nIndex++;
526       }
527       nStart = nIndex;
528       while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
529              nIndex - nStart < wCountM) {
530         if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
531           return false;
532         nOffsetM = spTime[nIndex] - '0' + nOffsetM * 10;
533         nIndex++;
534       }
535       if (nOffsetH > 12 || nOffsetM >= 60)
536         return false;
537     }
538   }
539   return nIndex == spTime.size() && wHour < 24 && wMinute < 60 &&
540          wSecond < 60 && wFraction <= 999;
541 }
542 
ParsePatternValue(const WideString & wsValue,const WideString & wsPattern,LocaleIface * pLocale)543 bool CXFA_LocaleValue::ParsePatternValue(const WideString& wsValue,
544                                          const WideString& wsPattern,
545                                          LocaleIface* pLocale) {
546   if (!m_pLocaleMgr)
547     return false;
548 
549   std::vector<WideString> wsPatterns =
550       CFGAS_StringFormatter::SplitOnBars(wsPattern);
551 
552   ScopedLocale scoped_locale(m_pLocaleMgr.Get(), pLocale);
553   bool bRet = false;
554   for (size_t i = 0; !bRet && i < wsPatterns.size(); i++) {
555     const WideString& wsFormat = wsPatterns[i];
556     auto pFormat =
557         pdfium::MakeUnique<CFGAS_StringFormatter>(m_pLocaleMgr.Get(), wsFormat);
558     switch (ValueCategory(pFormat->GetCategory(), m_dwType)) {
559       case FX_LOCALECATEGORY_Null:
560         bRet = pFormat->ParseNull(wsValue);
561         if (bRet)
562           m_wsValue.clear();
563         break;
564       case FX_LOCALECATEGORY_Zero:
565         bRet = pFormat->ParseZero(wsValue);
566         if (bRet)
567           m_wsValue = L"0";
568         break;
569       case FX_LOCALECATEGORY_Num: {
570         WideString fNum;
571         bRet = pFormat->ParseNum(wsValue, &fNum);
572         if (bRet)
573           m_wsValue = std::move(fNum);
574         break;
575       }
576       case FX_LOCALECATEGORY_Text:
577         bRet = pFormat->ParseText(wsValue, &m_wsValue);
578         break;
579       case FX_LOCALECATEGORY_Date: {
580         CFX_DateTime dt;
581         bRet = ValidateCanonicalDate(wsValue, &dt);
582         if (!bRet) {
583           bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Date, &dt);
584         }
585         if (bRet)
586           SetDate(dt);
587         break;
588       }
589       case FX_LOCALECATEGORY_Time: {
590         CFX_DateTime dt;
591         bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Time, &dt);
592         if (bRet)
593           SetTime(dt);
594         break;
595       }
596       case FX_LOCALECATEGORY_DateTime: {
597         CFX_DateTime dt;
598         bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_DateTime, &dt);
599         if (bRet)
600           SetDateTime(dt);
601         break;
602       }
603       default:
604         m_wsValue = wsValue;
605         bRet = true;
606         break;
607     }
608   }
609   if (!bRet)
610     m_wsValue = wsValue;
611 
612   return bRet;
613 }
614 
GetNumericFormat(WideString & wsFormat,int32_t nIntLen,int32_t nDecLen)615 void CXFA_LocaleValue::GetNumericFormat(WideString& wsFormat,
616                                         int32_t nIntLen,
617                                         int32_t nDecLen) {
618   ASSERT(wsFormat.IsEmpty());
619   ASSERT(nIntLen >= -1);
620   ASSERT(nDecLen >= -1);
621 
622   int32_t nTotalLen = (nIntLen >= 0 ? nIntLen : 2) + 1 +
623                       (nDecLen >= 0 ? nDecLen : 2) + (nDecLen == 0 ? 0 : 1);
624   {
625     // Span's lifetime must end before ReleaseBuffer() below.
626     pdfium::span<wchar_t> lpBuf = wsFormat.GetBuffer(nTotalLen);
627     int32_t nPos = 0;
628     lpBuf[nPos++] = L's';
629 
630     if (nIntLen == -1) {
631       lpBuf[nPos++] = L'z';
632       lpBuf[nPos++] = L'*';
633     } else {
634       while (nIntLen) {
635         lpBuf[nPos++] = L'z';
636         nIntLen--;
637       }
638     }
639     if (nDecLen != 0) {
640       lpBuf[nPos++] = L'.';
641     }
642     if (nDecLen == -1) {
643       lpBuf[nPos++] = L'z';
644       lpBuf[nPos++] = L'*';
645     } else {
646       while (nDecLen) {
647         lpBuf[nPos++] = L'z';
648         nDecLen--;
649       }
650     }
651   }
652   wsFormat.ReleaseBuffer(nTotalLen);
653 }
654 
ValidateNumericTemp(const WideString & wsNumeric,const WideString & wsFormat,LocaleIface * pLocale)655 bool CXFA_LocaleValue::ValidateNumericTemp(const WideString& wsNumeric,
656                                            const WideString& wsFormat,
657                                            LocaleIface* pLocale) {
658   if (wsFormat.IsEmpty() || wsNumeric.IsEmpty())
659     return true;
660 
661   pdfium::span<const wchar_t> spNum = wsNumeric.span();
662   pdfium::span<const wchar_t> spFmt = wsFormat.span();
663   int32_t n = 0;
664   int32_t nf = 0;
665   wchar_t c = spNum[n];
666   wchar_t cf = spFmt[nf];
667   if (cf == L's') {
668     if (c == L'-' || c == L'+')
669       ++n;
670     ++nf;
671   }
672 
673   bool bLimit = true;
674   int32_t nCount = wsNumeric.GetLength();
675   int32_t nCountFmt = wsFormat.GetLength();
676   while (n < nCount && (!bLimit || nf < nCountFmt) &&
677          FXSYS_IsDecimalDigit(c = spNum[n])) {
678     if (bLimit) {
679       if ((cf = spFmt[nf]) == L'*')
680         bLimit = false;
681       else if (cf == L'z')
682         nf++;
683       else
684         return false;
685     }
686     n++;
687   }
688   if (n == nCount)
689     return true;
690   if (nf == nCountFmt)
691     return false;
692 
693   while (nf < nCountFmt && (cf = spFmt[nf]) != L'.') {
694     ASSERT(cf == L'z' || cf == L'*');
695     ++nf;
696   }
697 
698   WideString wsDecimalSymbol;
699   if (pLocale)
700     wsDecimalSymbol = pLocale->GetDecimalSymbol();
701   else
702     wsDecimalSymbol = WideString(L'.');
703 
704   if (spFmt[nf] != L'.')
705     return false;
706   if (wsDecimalSymbol != WideStringView(c) && c != L'.')
707     return false;
708 
709   ++nf;
710   ++n;
711   bLimit = true;
712   while (n < nCount && (!bLimit || nf < nCountFmt) &&
713          FXSYS_IsDecimalDigit(spNum[n])) {
714     if (bLimit) {
715       if ((cf = spFmt[nf]) == L'*')
716         bLimit = false;
717       else if (cf == L'z')
718         nf++;
719       else
720         return false;
721     }
722     n++;
723   }
724   return n == nCount;
725 }
726