blob: e3c22c2976043e3db6384b3901137a6352811319 [file] [log] [blame]
K. Moon832a6942022-10-31 20:11:31 +00001// Copyright 2014 The PDFium Authors
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Lei Zhanga6d9f0e2015-06-13 00:48:38 -07004
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07005// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
Dan Sinclaire0345a42017-10-30 20:20:42 +00007#include "fxjs/cjs_util.h"
Tom Sepez37458412015-10-06 11:33:46 -07008
Lei Zhang15fef792021-08-04 17:52:39 +00009#include <math.h>
Lei Zhang6b37a5f2015-12-25 00:20:59 -080010#include <time.h>
11
tsepez86a61dc2016-03-25 10:00:11 -070012#include <algorithm>
Lei Zhang59a1a912021-06-07 16:55:25 +000013#include <string>
Dan Sinclair3ebd1212016-03-09 09:59:23 -050014#include <vector>
15
Lei Zhangdd77c862019-03-29 18:57:15 +000016#include "build/build_config.h"
Lei Zhang91085882024-02-22 00:49:57 +000017#include "core/fxcrt/check_op.h"
Tom Sepez6cfcf912024-06-07 00:30:07 +000018#include "core/fxcrt/compiler_specific.h"
Dan Sinclaircfb19442017-04-20 13:13:04 -040019#include "core/fxcrt/fx_extension.h"
Lei Zhang37935952024-02-16 04:39:56 +000020#include "core/fxcrt/span.h"
Dan Sinclaire0345a42017-10-30 20:20:42 +000021#include "fxjs/cjs_event_context.h"
Dan Sinclaire0345a42017-10-30 20:20:42 +000022#include "fxjs/cjs_object.h"
23#include "fxjs/cjs_publicmethods.h"
24#include "fxjs/cjs_runtime.h"
Tom Sepez86e5fbf2018-11-01 21:21:52 +000025#include "fxjs/fx_date_helpers.h"
Tom Sepezaf6371f2020-11-02 20:13:01 +000026#include "fxjs/fxv8.h"
Tom Sepez221f0b32018-06-04 22:11:27 +000027#include "fxjs/js_define.h"
Dan Sinclaire0345a42017-10-30 20:20:42 +000028#include "fxjs/js_resources.h"
Dan Elphick05673a32021-09-09 15:42:55 +000029#include "v8/include/v8-date.h"
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070030
Lei Zhangeb2da2a2022-01-12 19:28:33 +000031#if BUILDFLAG(IS_ANDROID)
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070032#include <ctype.h>
33#endif
34
tsepez86a61dc2016-03-25 10:00:11 -070035namespace {
36
37// Map PDF-style directives to equivalent wcsftime directives. Not
38// all have direct equivalents, though.
39struct TbConvert {
Dan Sinclair812e96c2017-03-13 16:43:37 -040040 const wchar_t* lpszJSMark;
41 const wchar_t* lpszCppMark;
tsepez86a61dc2016-03-25 10:00:11 -070042};
43
44// Map PDF-style directives lacking direct wcsftime directives to
45// the value with which they will be replaced.
46struct TbConvertAdditional {
Lei Zhang6b170cf2020-10-09 20:17:51 +000047 wchar_t js_mark;
48 int value;
tsepez86a61dc2016-03-25 10:00:11 -070049};
50
Tom Sepez666c4002024-04-04 21:24:15 +000051const TbConvert kTbConvertTable[] = {
tsepez86a61dc2016-03-25 10:00:11 -070052 {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"},
53 {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"},
54 {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"},
55 {L"TT", L"%p"},
Lei Zhangf997abe2022-01-12 19:14:23 +000056#if BUILDFLAG(IS_WIN)
tsepez86a61dc2016-03-25 10:00:11 -070057 {L"tt", L"%p"}, {L"h", L"%#I"},
58#else
59 {L"tt", L"%P"}, {L"h", L"%l"},
60#endif
61};
62
Tom Sepez9b86cb62018-11-01 19:11:15 +000063enum CaseMode { kPreserveCase, kUpperCase, kLowerCase };
64
65wchar_t TranslateCase(wchar_t input, CaseMode eMode) {
66 if (eMode == kLowerCase && FXSYS_iswupper(input))
67 return input | 0x20;
68 if (eMode == kUpperCase && FXSYS_iswlower(input))
69 return input & ~0x20;
70 return input;
71}
72
tsepez86a61dc2016-03-25 10:00:11 -070073} // namespace
74
Dan Sinclairc94a7932017-10-26 16:48:57 -040075const JSMethodSpec CJS_Util::MethodSpecs[] = {
Dan Sinclair909fa2d2017-12-12 01:53:28 +000076 {"printd", printd_static},
77 {"printf", printf_static},
78 {"printx", printx_static},
79 {"scand", scand_static},
80 {"byteToChar", byteToChar_static}};
Dan Sinclairef299532017-10-26 16:48:30 -040081
Tom Sepezb4958712020-10-13 20:30:43 +000082uint32_t CJS_Util::ObjDefnID = 0;
Dan Sinclairf7435522018-02-05 22:27:22 +000083const char CJS_Util::kName[] = "util";
Dan Sinclairef299532017-10-26 16:48:30 -040084
85// static
Tom Sepezb4958712020-10-13 20:30:43 +000086uint32_t CJS_Util::GetObjDefnID() {
Lei Zhangad1f7b42018-07-11 13:04:43 +000087 return ObjDefnID;
88}
89
90// static
Dan Sinclairbef4d3e2017-10-26 16:49:38 -040091void CJS_Util::DefineJSObjects(CFXJS_Engine* pEngine) {
Dan Sinclairf7435522018-02-05 22:27:22 +000092 ObjDefnID = pEngine->DefineObj(CJS_Util::kName, FXJSOBJTYPE_STATIC,
Dan Sinclair998fee32018-02-05 21:43:19 +000093 JSConstructor<CJS_Util>, JSDestructor);
Tom Sepez8b4ddeb2018-06-11 15:55:17 +000094 DefineMethods(pEngine, ObjDefnID, MethodSpecs);
Dan Sinclairef299532017-10-26 16:48:30 -040095}
96
Tom Sepez36aae4f2018-06-04 19:44:37 +000097CJS_Util::CJS_Util(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
98 : CJS_Object(pObject, pRuntime) {}
Dan Sinclair998fee32018-02-05 21:43:19 +000099
Dan Sinclairf7435522018-02-05 22:27:22 +0000100CJS_Util::~CJS_Util() = default;
tsepez86a61dc2016-03-25 10:00:11 -0700101
Tom Sepez3a6d0582018-08-17 19:28:52 +0000102CJS_Result CJS_Util::printf(CJS_Runtime* pRuntime,
Tom Sepez90589d42023-11-01 18:24:30 +0000103 pdfium::span<v8::Local<v8::Value>> params) {
Lei Zhang93fd8a82019-12-12 20:29:42 +0000104 const size_t num_params = params.size();
105 if (num_params < 1)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000106 return CJS_Result::Failure(JSMessage::kParamError);
Lei Zhang574b5742017-03-30 12:41:55 -0700107
Lei Zhang93fd8a82019-12-12 20:29:42 +0000108 // Use 'S' as a sentinel to ensure we always have some text before the first
109 // format specifier.
110 WideString unsafe_fmt_string = L'S' + pRuntime->ToWideString(params[0]);
111 std::vector<WideString> unsafe_conversion_specifiers;
112
113 {
114 size_t offset = 0;
115 while (true) {
Lei Zhang24c6be62024-02-08 20:06:48 +0000116 std::optional<size_t> offset_end =
Lei Zhang2c495302021-10-07 23:13:30 +0000117 unsafe_fmt_string.Find(L"%", offset + 1);
Lei Zhang93fd8a82019-12-12 20:29:42 +0000118 if (!offset_end.has_value()) {
119 unsafe_conversion_specifiers.push_back(
Daniel Hosseiniana9a704e2020-01-28 19:52:32 +0000120 unsafe_fmt_string.Last(unsafe_fmt_string.GetLength() - offset));
Lei Zhang93fd8a82019-12-12 20:29:42 +0000121 break;
122 }
123
124 unsafe_conversion_specifiers.push_back(
Daniel Hosseinian39516a52020-01-27 22:10:50 +0000125 unsafe_fmt_string.Substr(offset, offset_end.value() - offset));
Lei Zhang93fd8a82019-12-12 20:29:42 +0000126 offset = offset_end.value();
127 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700128 }
129
Lei Zhang93fd8a82019-12-12 20:29:42 +0000130 WideString result = unsafe_conversion_specifiers[0];
131 for (size_t i = 1; i < unsafe_conversion_specifiers.size(); ++i) {
132 WideString fmt = unsafe_conversion_specifiers[i];
133 if (i >= num_params) {
134 result += fmt;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700135 continue;
136 }
137
Lei Zhang93fd8a82019-12-12 20:29:42 +0000138 WideString segment;
139 switch (ParseDataType(&fmt)) {
Tom Sepez86390d72021-05-20 17:11:36 +0000140 case DataType::kInt:
Lei Zhang93fd8a82019-12-12 20:29:42 +0000141 segment = WideString::Format(fmt.c_str(), pRuntime->ToInt32(params[i]));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700142 break;
Tom Sepez86390d72021-05-20 17:11:36 +0000143 case DataType::kDouble:
Lei Zhang93fd8a82019-12-12 20:29:42 +0000144 segment =
145 WideString::Format(fmt.c_str(), pRuntime->ToDouble(params[i]));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700146 break;
Tom Sepez86390d72021-05-20 17:11:36 +0000147 case DataType::kString:
Lei Zhang93fd8a82019-12-12 20:29:42 +0000148 segment = WideString::Format(fmt.c_str(),
149 pRuntime->ToWideString(params[i]).c_str());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700150 break;
151 default:
Lei Zhang93fd8a82019-12-12 20:29:42 +0000152 segment = WideString::Format(L"%ls", fmt.c_str());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700153 break;
154 }
Lei Zhang93fd8a82019-12-12 20:29:42 +0000155 result += segment;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700156 }
157
Lei Zhang93fd8a82019-12-12 20:29:42 +0000158 // Remove the 'S' sentinel introduced earlier.
159 DCHECK_EQ(L'S', result[0]);
160 auto result_view = result.AsStringView();
161 return CJS_Result::Success(
Daniel Hosseiniana9a704e2020-01-28 19:52:32 +0000162 pRuntime->NewString(result_view.Last(result_view.GetLength() - 1)));
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700163}
164
Tom Sepez3a6d0582018-08-17 19:28:52 +0000165CJS_Result CJS_Util::printd(CJS_Runtime* pRuntime,
Tom Sepez90589d42023-11-01 18:24:30 +0000166 pdfium::span<v8::Local<v8::Value>> params) {
Lei Zhang574b5742017-03-30 12:41:55 -0700167 const size_t iSize = params.size();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700168 if (iSize < 2)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000169 return CJS_Result::Failure(JSMessage::kParamError);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700170
Tom Sepezaf6371f2020-11-02 20:13:01 +0000171 if (!fxv8::IsDate(params[1]))
Tom Sepez3a6d0582018-08-17 19:28:52 +0000172 return CJS_Result::Failure(JSMessage::kSecondParamNotDateError);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700173
Dan Sinclair6687f782017-10-25 14:34:26 -0400174 v8::Local<v8::Date> v8_date = params[1].As<v8::Date>();
Lei Zhang15fef792021-08-04 17:52:39 +0000175 if (v8_date.IsEmpty() || isnan(pRuntime->ToDouble(v8_date)))
Tom Sepez3a6d0582018-08-17 19:28:52 +0000176 return CJS_Result::Failure(JSMessage::kSecondParamInvalidDateError);
Dan Sinclair4c50b8b2017-10-25 15:36:11 -0400177
Tom Sepez86e5fbf2018-11-01 21:21:52 +0000178 double date = FX_LocalTime(pRuntime->ToDouble(v8_date));
179 int year = FX_GetYearFromTime(date);
180 int month = FX_GetMonthFromTime(date) + 1; // One-based.
181 int day = FX_GetDayFromTime(date);
182 int hour = FX_GetHourFromTime(date);
183 int min = FX_GetMinFromTime(date);
184 int sec = FX_GetSecFromTime(date);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700185
dan sinclair80435cb2017-10-24 21:40:24 -0400186 if (params[0]->IsNumber()) {
Ryan Harrison275e2602017-09-18 14:23:18 -0400187 WideString swResult;
dan sinclair80435cb2017-10-24 21:40:24 -0400188 switch (pRuntime->ToInt32(params[0])) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700189 case 0:
Dan Sinclair3f1c8322017-11-16 21:45:18 +0000190 swResult = WideString::Format(L"D:%04d%02d%02d%02d%02d%02d", year,
191 month, day, hour, min, sec);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700192 break;
193 case 1:
Dan Sinclair3f1c8322017-11-16 21:45:18 +0000194 swResult = WideString::Format(L"%04d.%02d.%02d %02d:%02d:%02d", year,
195 month, day, hour, min, sec);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700196 break;
197 case 2:
Dan Sinclair3f1c8322017-11-16 21:45:18 +0000198 swResult = WideString::Format(L"%04d/%02d/%02d %02d:%02d:%02d", year,
199 month, day, hour, min, sec);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700200 break;
201 default:
Tom Sepez3a6d0582018-08-17 19:28:52 +0000202 return CJS_Result::Failure(JSMessage::kValueError);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700203 }
204
Tom Sepez3a6d0582018-08-17 19:28:52 +0000205 return CJS_Result::Success(pRuntime->NewString(swResult.AsStringView()));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700206 }
tsepez86a61dc2016-03-25 10:00:11 -0700207
Tom Sepez20736f72018-08-17 16:44:50 +0000208 if (!params[0]->IsString())
Tom Sepez3a6d0582018-08-17 19:28:52 +0000209 return CJS_Result::Failure(JSMessage::kTypeError);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700210
Tom Sepez20736f72018-08-17 16:44:50 +0000211 // We don't support XFAPicture at the moment.
212 if (iSize > 2 && pRuntime->ToBoolean(params[2]))
Tom Sepez3a6d0582018-08-17 19:28:52 +0000213 return CJS_Result::Failure(JSMessage::kNotSupportedError);
tsepez86a61dc2016-03-25 10:00:11 -0700214
Tom Sepez20736f72018-08-17 16:44:50 +0000215 // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
216 // pre-existing %-directives before inserting our own.
Lei Zhang59a1a912021-06-07 16:55:25 +0000217 std::wstring cFormat = pRuntime->ToWideString(params[0]).c_str();
Tom Sepez20736f72018-08-17 16:44:50 +0000218 cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
219 cFormat.end());
220
Tom Sepez666c4002024-04-04 21:24:15 +0000221 for (const auto& conversion : kTbConvertTable) {
Tom Sepeza0de6da2021-11-11 01:40:23 +0000222 size_t nFound = 0;
Anton Bikineev7ac13342022-01-24 21:25:15 +0000223 while (true) {
Tom Sepez666c4002024-04-04 21:24:15 +0000224 nFound = cFormat.find(conversion.lpszJSMark, nFound);
225 if (nFound == std::wstring::npos) {
Tom Sepeza0de6da2021-11-11 01:40:23 +0000226 break;
Tom Sepez666c4002024-04-04 21:24:15 +0000227 }
228 cFormat.replace(nFound, wcslen(conversion.lpszJSMark),
229 conversion.lpszCppMark);
Tom Sepez20736f72018-08-17 16:44:50 +0000230 }
Lei Zhang002d9362018-12-08 00:04:41 +0000231 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700232
Lei Zhang002d9362018-12-08 00:04:41 +0000233 if (year < 0)
234 return CJS_Result::Failure(JSMessage::kValueError);
Lei Zhang2bf942d2017-06-16 13:48:19 -0700235
Tom Sepez666c4002024-04-04 21:24:15 +0000236 const TbConvertAdditional table_additional[] = {
Lei Zhang6b170cf2020-10-09 20:17:51 +0000237 {L'm', month}, {L'd', day},
238 {L'H', hour}, {L'h', hour > 12 ? hour - 12 : hour},
239 {L'M', min}, {L's', sec},
Lei Zhang002d9362018-12-08 00:04:41 +0000240 };
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700241
Tom Sepez666c4002024-04-04 21:24:15 +0000242 for (const auto& conversion : table_additional) {
Tom Sepeza0de6da2021-11-11 01:40:23 +0000243 size_t nFound = 0;
Anton Bikineev7ac13342022-01-24 21:25:15 +0000244 while (true) {
Tom Sepez666c4002024-04-04 21:24:15 +0000245 nFound = cFormat.find(conversion.js_mark, nFound);
246 if (nFound == std::wstring::npos) {
Tom Sepeza0de6da2021-11-11 01:40:23 +0000247 break;
Tom Sepez666c4002024-04-04 21:24:15 +0000248 }
Tom Sepeza0de6da2021-11-11 01:40:23 +0000249 if (nFound != 0 && cFormat[nFound - 1] == L'%') {
250 ++nFound;
251 continue;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700252 }
Tom Sepeza0de6da2021-11-11 01:40:23 +0000253 cFormat.replace(nFound, 1,
Tom Sepez666c4002024-04-04 21:24:15 +0000254 WideString::FormatInteger(conversion.value).c_str());
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700255 }
Lei Zhang002d9362018-12-08 00:04:41 +0000256 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700257
Lei Zhang002d9362018-12-08 00:04:41 +0000258 struct tm time = {};
259 time.tm_year = year - 1900;
260 time.tm_mon = month - 1;
261 time.tm_mday = day;
262 time.tm_hour = hour;
263 time.tm_min = min;
264 time.tm_sec = sec;
tsepez86a61dc2016-03-25 10:00:11 -0700265
Lei Zhang002d9362018-12-08 00:04:41 +0000266 wchar_t buf[64] = {};
Tom Sepez6cfcf912024-06-07 00:30:07 +0000267 UNSAFE_TODO(FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time));
Lei Zhang002d9362018-12-08 00:04:41 +0000268 cFormat = buf;
269 return CJS_Result::Success(pRuntime->NewString(cFormat.c_str()));
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700270}
271
Tom Sepez3a6d0582018-08-17 19:28:52 +0000272CJS_Result CJS_Util::printx(CJS_Runtime* pRuntime,
Tom Sepez90589d42023-11-01 18:24:30 +0000273 pdfium::span<v8::Local<v8::Value>> params) {
Dan Sinclair8f524d62017-10-25 13:30:31 -0400274 if (params.size() < 2)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000275 return CJS_Result::Failure(JSMessage::kParamError);
tsepezf3dc8c62016-08-10 06:29:29 -0700276
Tom Sepez3a6d0582018-08-17 19:28:52 +0000277 return CJS_Result::Success(
Tom Sepez9b86cb62018-11-01 19:11:15 +0000278 pRuntime->NewString(StringPrintx(pRuntime->ToWideString(params[0]),
279 pRuntime->ToWideString(params[1]))
Tom Sepezc62e8482018-06-20 20:32:04 +0000280 .AsStringView()));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700281}
282
Lei Zhang93fd8a82019-12-12 20:29:42 +0000283// static
Tom Sepez9b86cb62018-11-01 19:11:15 +0000284WideString CJS_Util::StringPrintx(const WideString& wsFormat,
285 const WideString& wsSource) {
Ryan Harrison275e2602017-09-18 14:23:18 -0400286 WideString wsResult;
Lei Zhang5d6714e2018-12-11 18:53:31 +0000287 wsResult.Reserve(wsFormat.GetLength());
Ryan Harrison875e98c2017-09-27 10:53:11 -0400288 size_t iSourceIdx = 0;
289 size_t iFormatIdx = 0;
tsepez4f1f41f2016-03-28 14:13:16 -0700290 CaseMode eCaseMode = kPreserveCase;
291 bool bEscaped = false;
292 while (iFormatIdx < wsFormat.GetLength()) {
293 if (bEscaped) {
294 bEscaped = false;
295 wsResult += wsFormat[iFormatIdx];
296 ++iFormatIdx;
297 continue;
298 }
299 switch (wsFormat[iFormatIdx]) {
300 case '\\': {
301 bEscaped = true;
302 ++iFormatIdx;
303 } break;
304 case '<': {
305 eCaseMode = kLowerCase;
306 ++iFormatIdx;
307 } break;
308 case '>': {
309 eCaseMode = kUpperCase;
310 ++iFormatIdx;
311 } break;
312 case '=': {
313 eCaseMode = kPreserveCase;
314 ++iFormatIdx;
315 } break;
316 case '?': {
317 if (iSourceIdx < wsSource.GetLength()) {
318 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
319 ++iSourceIdx;
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700320 }
tsepez4f1f41f2016-03-28 14:13:16 -0700321 ++iFormatIdx;
322 } break;
323 case 'X': {
324 if (iSourceIdx < wsSource.GetLength()) {
Ryan Harrison15c0fcc2018-03-12 15:20:04 +0000325 if (isascii(wsSource[iSourceIdx]) && isalnum(wsSource[iSourceIdx])) {
tsepez4f1f41f2016-03-28 14:13:16 -0700326 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
327 ++iFormatIdx;
328 }
329 ++iSourceIdx;
330 } else {
331 ++iFormatIdx;
332 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700333 } break;
334 case 'A': {
tsepez4f1f41f2016-03-28 14:13:16 -0700335 if (iSourceIdx < wsSource.GetLength()) {
Ryan Harrison735eda92018-03-12 15:44:36 +0000336 if (isascii(wsSource[iSourceIdx]) && isalpha(wsSource[iSourceIdx])) {
tsepez4f1f41f2016-03-28 14:13:16 -0700337 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
338 ++iFormatIdx;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700339 }
tsepez4f1f41f2016-03-28 14:13:16 -0700340 ++iSourceIdx;
341 } else {
342 ++iFormatIdx;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700343 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700344 } break;
345 case '9': {
tsepez4f1f41f2016-03-28 14:13:16 -0700346 if (iSourceIdx < wsSource.GetLength()) {
Lei Zhang4609c5d2018-12-07 20:10:54 +0000347 if (FXSYS_IsDecimalDigit(wsSource[iSourceIdx])) {
tsepez4f1f41f2016-03-28 14:13:16 -0700348 wsResult += wsSource[iSourceIdx];
349 ++iFormatIdx;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700350 }
tsepez4f1f41f2016-03-28 14:13:16 -0700351 ++iSourceIdx;
352 } else {
353 ++iFormatIdx;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700354 }
tsepez4f1f41f2016-03-28 14:13:16 -0700355 } break;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700356 case '*': {
tsepez4f1f41f2016-03-28 14:13:16 -0700357 if (iSourceIdx < wsSource.GetLength()) {
358 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
359 ++iSourceIdx;
360 } else {
361 ++iFormatIdx;
362 }
363 } break;
364 default: {
365 wsResult += wsFormat[iFormatIdx];
366 ++iFormatIdx;
367 } break;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700368 }
369 }
tsepez4f1f41f2016-03-28 14:13:16 -0700370 return wsResult;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700371}
372
Tom Sepez3a6d0582018-08-17 19:28:52 +0000373CJS_Result CJS_Util::scand(CJS_Runtime* pRuntime,
Tom Sepez90589d42023-11-01 18:24:30 +0000374 pdfium::span<v8::Local<v8::Value>> params) {
Lei Zhang574b5742017-03-30 12:41:55 -0700375 if (params.size() < 2)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000376 return CJS_Result::Failure(JSMessage::kParamError);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700377
dan sinclair80435cb2017-10-24 21:40:24 -0400378 WideString sFormat = pRuntime->ToWideString(params[0]);
379 WideString sDate = pRuntime->ToWideString(params[1]);
Tom Sepez86e5fbf2018-11-01 21:21:52 +0000380 double dDate = FX_GetDateTime();
Dan Sinclair8f524d62017-10-25 13:30:31 -0400381 if (sDate.GetLength() > 0)
Daniel Hosseinian4aa51a52021-10-22 17:32:43 +0000382 dDate = CJS_PublicMethods::ParseDateUsingFormat(pRuntime->GetIsolate(),
383 sDate, sFormat, nullptr);
Lei Zhang15fef792021-08-04 17:52:39 +0000384 if (isnan(dDate))
Tom Sepez3a6d0582018-08-17 19:28:52 +0000385 return CJS_Result::Success(pRuntime->NewUndefined());
Tom Sepez20736f72018-08-17 16:44:50 +0000386
Tom Sepez3a6d0582018-08-17 19:28:52 +0000387 return CJS_Result::Success(pRuntime->NewDate(dDate));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700388}
389
Tom Sepez90589d42023-11-01 18:24:30 +0000390CJS_Result CJS_Util::byteToChar(CJS_Runtime* pRuntime,
391 pdfium::span<v8::Local<v8::Value>> params) {
Dan Sinclair8f524d62017-10-25 13:30:31 -0400392 if (params.size() < 1)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000393 return CJS_Result::Failure(JSMessage::kParamError);
tsepezf3dc8c62016-08-10 06:29:29 -0700394
dan sinclair80435cb2017-10-24 21:40:24 -0400395 int arg = pRuntime->ToInt32(params[0]);
Dan Sinclair8f524d62017-10-25 13:30:31 -0400396 if (arg < 0 || arg > 255)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000397 return CJS_Result::Failure(JSMessage::kValueError);
tsepezf3dc8c62016-08-10 06:29:29 -0700398
Ryan Harrison275e2602017-09-18 14:23:18 -0400399 WideString wStr(static_cast<wchar_t>(arg));
Tom Sepez3a6d0582018-08-17 19:28:52 +0000400 return CJS_Result::Success(pRuntime->NewString(wStr.AsStringView()));
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700401}
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400402
Lei Zhang93fd8a82019-12-12 20:29:42 +0000403// static
Tom Sepez86390d72021-05-20 17:11:36 +0000404CJS_Util::DataType CJS_Util::ParseDataType(WideString* sFormat) {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000405 enum State { kBefore, kFlags, kWidth, kPrecision, kSpecifier, kAfter };
Tom Sepezffbc0d92017-07-17 09:29:05 -0700406
Tom Sepez86390d72021-05-20 17:11:36 +0000407 DataType result = DataType::kInvalid;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000408 State state = kBefore;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700409 size_t precision_digits = 0;
410 size_t i = 0;
Lei Zhang93fd8a82019-12-12 20:29:42 +0000411 while (i < sFormat->GetLength()) {
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400412 wchar_t c = (*sFormat)[i];
Tom Sepezffbc0d92017-07-17 09:29:05 -0700413 switch (state) {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000414 case kBefore:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700415 if (c == L'%')
Lei Zhang3f1101c2021-06-14 16:51:17 +0000416 state = kFlags;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700417 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000418 case kFlags:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700419 if (c == L'+' || c == L'-' || c == L'#' || c == L' ') {
420 // Stay in same state.
421 } else {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000422 state = kWidth;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700423 continue; // Re-process same character.
424 }
425 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000426 case kWidth:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700427 if (c == L'*')
Tom Sepez86390d72021-05-20 17:11:36 +0000428 return DataType::kInvalid;
Lei Zhang4609c5d2018-12-07 20:10:54 +0000429 if (FXSYS_IsDecimalDigit(c)) {
Tom Sepezffbc0d92017-07-17 09:29:05 -0700430 // Stay in same state.
431 } else if (c == L'.') {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000432 state = kPrecision;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700433 } else {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000434 state = kSpecifier;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700435 continue; // Re-process same character.
436 }
437 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000438 case kPrecision:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700439 if (c == L'*')
Tom Sepez86390d72021-05-20 17:11:36 +0000440 return DataType::kInvalid;
Lei Zhang4609c5d2018-12-07 20:10:54 +0000441 if (FXSYS_IsDecimalDigit(c)) {
Tom Sepezffbc0d92017-07-17 09:29:05 -0700442 // Stay in same state.
443 ++precision_digits;
444 } else {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000445 state = kSpecifier;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700446 continue; // Re-process same character.
447 }
448 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000449 case kSpecifier:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700450 if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' ||
451 c == L'u' || c == L'x' || c == L'X') {
Tom Sepez86390d72021-05-20 17:11:36 +0000452 result = DataType::kInt;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700453 } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' ||
454 c == L'G') {
Tom Sepez86390d72021-05-20 17:11:36 +0000455 result = DataType::kDouble;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700456 } else if (c == L's' || c == L'S') {
457 // Map s to S since we always deal internally with wchar_t strings.
458 // TODO(tsepez): Probably 100% borked. %S is not a standard
459 // conversion.
Lei Zhang93fd8a82019-12-12 20:29:42 +0000460 sFormat->SetAt(i, L'S');
Tom Sepez86390d72021-05-20 17:11:36 +0000461 result = DataType::kString;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700462 } else {
Tom Sepez86390d72021-05-20 17:11:36 +0000463 return DataType::kInvalid;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700464 }
Lei Zhang3f1101c2021-06-14 16:51:17 +0000465 state = kAfter;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700466 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000467 case kAfter:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700468 if (c == L'%')
Tom Sepez86390d72021-05-20 17:11:36 +0000469 return DataType::kInvalid;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700470 // Stay in same state until string exhausted.
471 break;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400472 }
Tom Sepezffbc0d92017-07-17 09:29:05 -0700473 ++i;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400474 }
Tom Sepezffbc0d92017-07-17 09:29:05 -0700475 // See https://crbug.com/740166
Tom Sepez86390d72021-05-20 17:11:36 +0000476 if (result == DataType::kInt && precision_digits > 2)
477 return DataType::kInvalid;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400478
Tom Sepezffbc0d92017-07-17 09:29:05 -0700479 return result;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400480}