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