1*3ac0a46fSAndroid Build Coastguard Worker // Copyright 2014 The PDFium Authors
2*3ac0a46fSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*3ac0a46fSAndroid Build Coastguard Worker // found in the LICENSE file.
4*3ac0a46fSAndroid Build Coastguard Worker
5*3ac0a46fSAndroid Build Coastguard Worker // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6*3ac0a46fSAndroid Build Coastguard Worker
7*3ac0a46fSAndroid Build Coastguard Worker #include "fxjs/cjs_util.h"
8*3ac0a46fSAndroid Build Coastguard Worker
9*3ac0a46fSAndroid Build Coastguard Worker #include <math.h>
10*3ac0a46fSAndroid Build Coastguard Worker #include <time.h>
11*3ac0a46fSAndroid Build Coastguard Worker
12*3ac0a46fSAndroid Build Coastguard Worker #include <algorithm>
13*3ac0a46fSAndroid Build Coastguard Worker #include <string>
14*3ac0a46fSAndroid Build Coastguard Worker #include <vector>
15*3ac0a46fSAndroid Build Coastguard Worker
16*3ac0a46fSAndroid Build Coastguard Worker #include "build/build_config.h"
17*3ac0a46fSAndroid Build Coastguard Worker #include "core/fxcrt/fx_extension.h"
18*3ac0a46fSAndroid Build Coastguard Worker #include "fxjs/cjs_event_context.h"
19*3ac0a46fSAndroid Build Coastguard Worker #include "fxjs/cjs_object.h"
20*3ac0a46fSAndroid Build Coastguard Worker #include "fxjs/cjs_publicmethods.h"
21*3ac0a46fSAndroid Build Coastguard Worker #include "fxjs/cjs_runtime.h"
22*3ac0a46fSAndroid Build Coastguard Worker #include "fxjs/fx_date_helpers.h"
23*3ac0a46fSAndroid Build Coastguard Worker #include "fxjs/fxv8.h"
24*3ac0a46fSAndroid Build Coastguard Worker #include "fxjs/js_define.h"
25*3ac0a46fSAndroid Build Coastguard Worker #include "fxjs/js_resources.h"
26*3ac0a46fSAndroid Build Coastguard Worker #include "third_party/base/check_op.h"
27*3ac0a46fSAndroid Build Coastguard Worker #include "v8/include/v8-date.h"
28*3ac0a46fSAndroid Build Coastguard Worker
29*3ac0a46fSAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
30*3ac0a46fSAndroid Build Coastguard Worker #include <ctype.h>
31*3ac0a46fSAndroid Build Coastguard Worker #endif
32*3ac0a46fSAndroid Build Coastguard Worker
33*3ac0a46fSAndroid Build Coastguard Worker namespace {
34*3ac0a46fSAndroid Build Coastguard Worker
35*3ac0a46fSAndroid Build Coastguard Worker // Map PDF-style directives to equivalent wcsftime directives. Not
36*3ac0a46fSAndroid Build Coastguard Worker // all have direct equivalents, though.
37*3ac0a46fSAndroid Build Coastguard Worker struct TbConvert {
38*3ac0a46fSAndroid Build Coastguard Worker const wchar_t* lpszJSMark;
39*3ac0a46fSAndroid Build Coastguard Worker const wchar_t* lpszCppMark;
40*3ac0a46fSAndroid Build Coastguard Worker };
41*3ac0a46fSAndroid Build Coastguard Worker
42*3ac0a46fSAndroid Build Coastguard Worker // Map PDF-style directives lacking direct wcsftime directives to
43*3ac0a46fSAndroid Build Coastguard Worker // the value with which they will be replaced.
44*3ac0a46fSAndroid Build Coastguard Worker struct TbConvertAdditional {
45*3ac0a46fSAndroid Build Coastguard Worker wchar_t js_mark;
46*3ac0a46fSAndroid Build Coastguard Worker int value;
47*3ac0a46fSAndroid Build Coastguard Worker };
48*3ac0a46fSAndroid Build Coastguard Worker
49*3ac0a46fSAndroid Build Coastguard Worker const TbConvert TbConvertTable[] = {
50*3ac0a46fSAndroid Build Coastguard Worker {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"},
51*3ac0a46fSAndroid Build Coastguard Worker {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"},
52*3ac0a46fSAndroid Build Coastguard Worker {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"},
53*3ac0a46fSAndroid Build Coastguard Worker {L"TT", L"%p"},
54*3ac0a46fSAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
55*3ac0a46fSAndroid Build Coastguard Worker {L"tt", L"%p"}, {L"h", L"%#I"},
56*3ac0a46fSAndroid Build Coastguard Worker #else
57*3ac0a46fSAndroid Build Coastguard Worker {L"tt", L"%P"}, {L"h", L"%l"},
58*3ac0a46fSAndroid Build Coastguard Worker #endif
59*3ac0a46fSAndroid Build Coastguard Worker };
60*3ac0a46fSAndroid Build Coastguard Worker
61*3ac0a46fSAndroid Build Coastguard Worker enum CaseMode { kPreserveCase, kUpperCase, kLowerCase };
62*3ac0a46fSAndroid Build Coastguard Worker
TranslateCase(wchar_t input,CaseMode eMode)63*3ac0a46fSAndroid Build Coastguard Worker wchar_t TranslateCase(wchar_t input, CaseMode eMode) {
64*3ac0a46fSAndroid Build Coastguard Worker if (eMode == kLowerCase && FXSYS_iswupper(input))
65*3ac0a46fSAndroid Build Coastguard Worker return input | 0x20;
66*3ac0a46fSAndroid Build Coastguard Worker if (eMode == kUpperCase && FXSYS_iswlower(input))
67*3ac0a46fSAndroid Build Coastguard Worker return input & ~0x20;
68*3ac0a46fSAndroid Build Coastguard Worker return input;
69*3ac0a46fSAndroid Build Coastguard Worker }
70*3ac0a46fSAndroid Build Coastguard Worker
71*3ac0a46fSAndroid Build Coastguard Worker } // namespace
72*3ac0a46fSAndroid Build Coastguard Worker
73*3ac0a46fSAndroid Build Coastguard Worker const JSMethodSpec CJS_Util::MethodSpecs[] = {
74*3ac0a46fSAndroid Build Coastguard Worker {"printd", printd_static},
75*3ac0a46fSAndroid Build Coastguard Worker {"printf", printf_static},
76*3ac0a46fSAndroid Build Coastguard Worker {"printx", printx_static},
77*3ac0a46fSAndroid Build Coastguard Worker {"scand", scand_static},
78*3ac0a46fSAndroid Build Coastguard Worker {"byteToChar", byteToChar_static}};
79*3ac0a46fSAndroid Build Coastguard Worker
80*3ac0a46fSAndroid Build Coastguard Worker uint32_t CJS_Util::ObjDefnID = 0;
81*3ac0a46fSAndroid Build Coastguard Worker const char CJS_Util::kName[] = "util";
82*3ac0a46fSAndroid Build Coastguard Worker
83*3ac0a46fSAndroid Build Coastguard Worker // static
GetObjDefnID()84*3ac0a46fSAndroid Build Coastguard Worker uint32_t CJS_Util::GetObjDefnID() {
85*3ac0a46fSAndroid Build Coastguard Worker return ObjDefnID;
86*3ac0a46fSAndroid Build Coastguard Worker }
87*3ac0a46fSAndroid Build Coastguard Worker
88*3ac0a46fSAndroid Build Coastguard Worker // static
DefineJSObjects(CFXJS_Engine * pEngine)89*3ac0a46fSAndroid Build Coastguard Worker void CJS_Util::DefineJSObjects(CFXJS_Engine* pEngine) {
90*3ac0a46fSAndroid Build Coastguard Worker ObjDefnID = pEngine->DefineObj(CJS_Util::kName, FXJSOBJTYPE_STATIC,
91*3ac0a46fSAndroid Build Coastguard Worker JSConstructor<CJS_Util>, JSDestructor);
92*3ac0a46fSAndroid Build Coastguard Worker DefineMethods(pEngine, ObjDefnID, MethodSpecs);
93*3ac0a46fSAndroid Build Coastguard Worker }
94*3ac0a46fSAndroid Build Coastguard Worker
CJS_Util(v8::Local<v8::Object> pObject,CJS_Runtime * pRuntime)95*3ac0a46fSAndroid Build Coastguard Worker CJS_Util::CJS_Util(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
96*3ac0a46fSAndroid Build Coastguard Worker : CJS_Object(pObject, pRuntime) {}
97*3ac0a46fSAndroid Build Coastguard Worker
98*3ac0a46fSAndroid Build Coastguard Worker CJS_Util::~CJS_Util() = default;
99*3ac0a46fSAndroid Build Coastguard Worker
printf(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)100*3ac0a46fSAndroid Build Coastguard Worker CJS_Result CJS_Util::printf(CJS_Runtime* pRuntime,
101*3ac0a46fSAndroid Build Coastguard Worker const std::vector<v8::Local<v8::Value>>& params) {
102*3ac0a46fSAndroid Build Coastguard Worker const size_t num_params = params.size();
103*3ac0a46fSAndroid Build Coastguard Worker if (num_params < 1)
104*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kParamError);
105*3ac0a46fSAndroid Build Coastguard Worker
106*3ac0a46fSAndroid Build Coastguard Worker // Use 'S' as a sentinel to ensure we always have some text before the first
107*3ac0a46fSAndroid Build Coastguard Worker // format specifier.
108*3ac0a46fSAndroid Build Coastguard Worker WideString unsafe_fmt_string = L'S' + pRuntime->ToWideString(params[0]);
109*3ac0a46fSAndroid Build Coastguard Worker std::vector<WideString> unsafe_conversion_specifiers;
110*3ac0a46fSAndroid Build Coastguard Worker
111*3ac0a46fSAndroid Build Coastguard Worker {
112*3ac0a46fSAndroid Build Coastguard Worker size_t offset = 0;
113*3ac0a46fSAndroid Build Coastguard Worker while (true) {
114*3ac0a46fSAndroid Build Coastguard Worker absl::optional<size_t> offset_end =
115*3ac0a46fSAndroid Build Coastguard Worker unsafe_fmt_string.Find(L"%", offset + 1);
116*3ac0a46fSAndroid Build Coastguard Worker if (!offset_end.has_value()) {
117*3ac0a46fSAndroid Build Coastguard Worker unsafe_conversion_specifiers.push_back(
118*3ac0a46fSAndroid Build Coastguard Worker unsafe_fmt_string.Last(unsafe_fmt_string.GetLength() - offset));
119*3ac0a46fSAndroid Build Coastguard Worker break;
120*3ac0a46fSAndroid Build Coastguard Worker }
121*3ac0a46fSAndroid Build Coastguard Worker
122*3ac0a46fSAndroid Build Coastguard Worker unsafe_conversion_specifiers.push_back(
123*3ac0a46fSAndroid Build Coastguard Worker unsafe_fmt_string.Substr(offset, offset_end.value() - offset));
124*3ac0a46fSAndroid Build Coastguard Worker offset = offset_end.value();
125*3ac0a46fSAndroid Build Coastguard Worker }
126*3ac0a46fSAndroid Build Coastguard Worker }
127*3ac0a46fSAndroid Build Coastguard Worker
128*3ac0a46fSAndroid Build Coastguard Worker WideString result = unsafe_conversion_specifiers[0];
129*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 1; i < unsafe_conversion_specifiers.size(); ++i) {
130*3ac0a46fSAndroid Build Coastguard Worker WideString fmt = unsafe_conversion_specifiers[i];
131*3ac0a46fSAndroid Build Coastguard Worker if (i >= num_params) {
132*3ac0a46fSAndroid Build Coastguard Worker result += fmt;
133*3ac0a46fSAndroid Build Coastguard Worker continue;
134*3ac0a46fSAndroid Build Coastguard Worker }
135*3ac0a46fSAndroid Build Coastguard Worker
136*3ac0a46fSAndroid Build Coastguard Worker WideString segment;
137*3ac0a46fSAndroid Build Coastguard Worker switch (ParseDataType(&fmt)) {
138*3ac0a46fSAndroid Build Coastguard Worker case DataType::kInt:
139*3ac0a46fSAndroid Build Coastguard Worker segment = WideString::Format(fmt.c_str(), pRuntime->ToInt32(params[i]));
140*3ac0a46fSAndroid Build Coastguard Worker break;
141*3ac0a46fSAndroid Build Coastguard Worker case DataType::kDouble:
142*3ac0a46fSAndroid Build Coastguard Worker segment =
143*3ac0a46fSAndroid Build Coastguard Worker WideString::Format(fmt.c_str(), pRuntime->ToDouble(params[i]));
144*3ac0a46fSAndroid Build Coastguard Worker break;
145*3ac0a46fSAndroid Build Coastguard Worker case DataType::kString:
146*3ac0a46fSAndroid Build Coastguard Worker segment = WideString::Format(fmt.c_str(),
147*3ac0a46fSAndroid Build Coastguard Worker pRuntime->ToWideString(params[i]).c_str());
148*3ac0a46fSAndroid Build Coastguard Worker break;
149*3ac0a46fSAndroid Build Coastguard Worker default:
150*3ac0a46fSAndroid Build Coastguard Worker segment = WideString::Format(L"%ls", fmt.c_str());
151*3ac0a46fSAndroid Build Coastguard Worker break;
152*3ac0a46fSAndroid Build Coastguard Worker }
153*3ac0a46fSAndroid Build Coastguard Worker result += segment;
154*3ac0a46fSAndroid Build Coastguard Worker }
155*3ac0a46fSAndroid Build Coastguard Worker
156*3ac0a46fSAndroid Build Coastguard Worker // Remove the 'S' sentinel introduced earlier.
157*3ac0a46fSAndroid Build Coastguard Worker DCHECK_EQ(L'S', result[0]);
158*3ac0a46fSAndroid Build Coastguard Worker auto result_view = result.AsStringView();
159*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Success(
160*3ac0a46fSAndroid Build Coastguard Worker pRuntime->NewString(result_view.Last(result_view.GetLength() - 1)));
161*3ac0a46fSAndroid Build Coastguard Worker }
162*3ac0a46fSAndroid Build Coastguard Worker
printd(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)163*3ac0a46fSAndroid Build Coastguard Worker CJS_Result CJS_Util::printd(CJS_Runtime* pRuntime,
164*3ac0a46fSAndroid Build Coastguard Worker const std::vector<v8::Local<v8::Value>>& params) {
165*3ac0a46fSAndroid Build Coastguard Worker const size_t iSize = params.size();
166*3ac0a46fSAndroid Build Coastguard Worker if (iSize < 2)
167*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kParamError);
168*3ac0a46fSAndroid Build Coastguard Worker
169*3ac0a46fSAndroid Build Coastguard Worker if (!fxv8::IsDate(params[1]))
170*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kSecondParamNotDateError);
171*3ac0a46fSAndroid Build Coastguard Worker
172*3ac0a46fSAndroid Build Coastguard Worker v8::Local<v8::Date> v8_date = params[1].As<v8::Date>();
173*3ac0a46fSAndroid Build Coastguard Worker if (v8_date.IsEmpty() || isnan(pRuntime->ToDouble(v8_date)))
174*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kSecondParamInvalidDateError);
175*3ac0a46fSAndroid Build Coastguard Worker
176*3ac0a46fSAndroid Build Coastguard Worker double date = FX_LocalTime(pRuntime->ToDouble(v8_date));
177*3ac0a46fSAndroid Build Coastguard Worker int year = FX_GetYearFromTime(date);
178*3ac0a46fSAndroid Build Coastguard Worker int month = FX_GetMonthFromTime(date) + 1; // One-based.
179*3ac0a46fSAndroid Build Coastguard Worker int day = FX_GetDayFromTime(date);
180*3ac0a46fSAndroid Build Coastguard Worker int hour = FX_GetHourFromTime(date);
181*3ac0a46fSAndroid Build Coastguard Worker int min = FX_GetMinFromTime(date);
182*3ac0a46fSAndroid Build Coastguard Worker int sec = FX_GetSecFromTime(date);
183*3ac0a46fSAndroid Build Coastguard Worker
184*3ac0a46fSAndroid Build Coastguard Worker if (params[0]->IsNumber()) {
185*3ac0a46fSAndroid Build Coastguard Worker WideString swResult;
186*3ac0a46fSAndroid Build Coastguard Worker switch (pRuntime->ToInt32(params[0])) {
187*3ac0a46fSAndroid Build Coastguard Worker case 0:
188*3ac0a46fSAndroid Build Coastguard Worker swResult = WideString::Format(L"D:%04d%02d%02d%02d%02d%02d", year,
189*3ac0a46fSAndroid Build Coastguard Worker month, day, hour, min, sec);
190*3ac0a46fSAndroid Build Coastguard Worker break;
191*3ac0a46fSAndroid Build Coastguard Worker case 1:
192*3ac0a46fSAndroid Build Coastguard Worker swResult = WideString::Format(L"%04d.%02d.%02d %02d:%02d:%02d", year,
193*3ac0a46fSAndroid Build Coastguard Worker month, day, hour, min, sec);
194*3ac0a46fSAndroid Build Coastguard Worker break;
195*3ac0a46fSAndroid Build Coastguard Worker case 2:
196*3ac0a46fSAndroid Build Coastguard Worker swResult = WideString::Format(L"%04d/%02d/%02d %02d:%02d:%02d", year,
197*3ac0a46fSAndroid Build Coastguard Worker month, day, hour, min, sec);
198*3ac0a46fSAndroid Build Coastguard Worker break;
199*3ac0a46fSAndroid Build Coastguard Worker default:
200*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kValueError);
201*3ac0a46fSAndroid Build Coastguard Worker }
202*3ac0a46fSAndroid Build Coastguard Worker
203*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Success(pRuntime->NewString(swResult.AsStringView()));
204*3ac0a46fSAndroid Build Coastguard Worker }
205*3ac0a46fSAndroid Build Coastguard Worker
206*3ac0a46fSAndroid Build Coastguard Worker if (!params[0]->IsString())
207*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kTypeError);
208*3ac0a46fSAndroid Build Coastguard Worker
209*3ac0a46fSAndroid Build Coastguard Worker // We don't support XFAPicture at the moment.
210*3ac0a46fSAndroid Build Coastguard Worker if (iSize > 2 && pRuntime->ToBoolean(params[2]))
211*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kNotSupportedError);
212*3ac0a46fSAndroid Build Coastguard Worker
213*3ac0a46fSAndroid Build Coastguard Worker // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
214*3ac0a46fSAndroid Build Coastguard Worker // pre-existing %-directives before inserting our own.
215*3ac0a46fSAndroid Build Coastguard Worker std::wstring cFormat = pRuntime->ToWideString(params[0]).c_str();
216*3ac0a46fSAndroid Build Coastguard Worker cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
217*3ac0a46fSAndroid Build Coastguard Worker cFormat.end());
218*3ac0a46fSAndroid Build Coastguard Worker
219*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(TbConvertTable); ++i) {
220*3ac0a46fSAndroid Build Coastguard Worker size_t nFound = 0;
221*3ac0a46fSAndroid Build Coastguard Worker while (true) {
222*3ac0a46fSAndroid Build Coastguard Worker nFound = cFormat.find(TbConvertTable[i].lpszJSMark, nFound);
223*3ac0a46fSAndroid Build Coastguard Worker if (nFound == std::wstring::npos)
224*3ac0a46fSAndroid Build Coastguard Worker break;
225*3ac0a46fSAndroid Build Coastguard Worker
226*3ac0a46fSAndroid Build Coastguard Worker cFormat.replace(nFound, wcslen(TbConvertTable[i].lpszJSMark),
227*3ac0a46fSAndroid Build Coastguard Worker TbConvertTable[i].lpszCppMark);
228*3ac0a46fSAndroid Build Coastguard Worker }
229*3ac0a46fSAndroid Build Coastguard Worker }
230*3ac0a46fSAndroid Build Coastguard Worker
231*3ac0a46fSAndroid Build Coastguard Worker if (year < 0)
232*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kValueError);
233*3ac0a46fSAndroid Build Coastguard Worker
234*3ac0a46fSAndroid Build Coastguard Worker const TbConvertAdditional cTableAd[] = {
235*3ac0a46fSAndroid Build Coastguard Worker {L'm', month}, {L'd', day},
236*3ac0a46fSAndroid Build Coastguard Worker {L'H', hour}, {L'h', hour > 12 ? hour - 12 : hour},
237*3ac0a46fSAndroid Build Coastguard Worker {L'M', min}, {L's', sec},
238*3ac0a46fSAndroid Build Coastguard Worker };
239*3ac0a46fSAndroid Build Coastguard Worker
240*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(cTableAd); ++i) {
241*3ac0a46fSAndroid Build Coastguard Worker size_t nFound = 0;
242*3ac0a46fSAndroid Build Coastguard Worker while (true) {
243*3ac0a46fSAndroid Build Coastguard Worker nFound = cFormat.find(cTableAd[i].js_mark, nFound);
244*3ac0a46fSAndroid Build Coastguard Worker if (nFound == std::wstring::npos)
245*3ac0a46fSAndroid Build Coastguard Worker break;
246*3ac0a46fSAndroid Build Coastguard Worker
247*3ac0a46fSAndroid Build Coastguard Worker if (nFound != 0 && cFormat[nFound - 1] == L'%') {
248*3ac0a46fSAndroid Build Coastguard Worker ++nFound;
249*3ac0a46fSAndroid Build Coastguard Worker continue;
250*3ac0a46fSAndroid Build Coastguard Worker }
251*3ac0a46fSAndroid Build Coastguard Worker cFormat.replace(nFound, 1,
252*3ac0a46fSAndroid Build Coastguard Worker WideString::FormatInteger(cTableAd[i].value).c_str());
253*3ac0a46fSAndroid Build Coastguard Worker }
254*3ac0a46fSAndroid Build Coastguard Worker }
255*3ac0a46fSAndroid Build Coastguard Worker
256*3ac0a46fSAndroid Build Coastguard Worker struct tm time = {};
257*3ac0a46fSAndroid Build Coastguard Worker time.tm_year = year - 1900;
258*3ac0a46fSAndroid Build Coastguard Worker time.tm_mon = month - 1;
259*3ac0a46fSAndroid Build Coastguard Worker time.tm_mday = day;
260*3ac0a46fSAndroid Build Coastguard Worker time.tm_hour = hour;
261*3ac0a46fSAndroid Build Coastguard Worker time.tm_min = min;
262*3ac0a46fSAndroid Build Coastguard Worker time.tm_sec = sec;
263*3ac0a46fSAndroid Build Coastguard Worker
264*3ac0a46fSAndroid Build Coastguard Worker wchar_t buf[64] = {};
265*3ac0a46fSAndroid Build Coastguard Worker FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time);
266*3ac0a46fSAndroid Build Coastguard Worker cFormat = buf;
267*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Success(pRuntime->NewString(cFormat.c_str()));
268*3ac0a46fSAndroid Build Coastguard Worker }
269*3ac0a46fSAndroid Build Coastguard Worker
printx(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)270*3ac0a46fSAndroid Build Coastguard Worker CJS_Result CJS_Util::printx(CJS_Runtime* pRuntime,
271*3ac0a46fSAndroid Build Coastguard Worker const std::vector<v8::Local<v8::Value>>& params) {
272*3ac0a46fSAndroid Build Coastguard Worker if (params.size() < 2)
273*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kParamError);
274*3ac0a46fSAndroid Build Coastguard Worker
275*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Success(
276*3ac0a46fSAndroid Build Coastguard Worker pRuntime->NewString(StringPrintx(pRuntime->ToWideString(params[0]),
277*3ac0a46fSAndroid Build Coastguard Worker pRuntime->ToWideString(params[1]))
278*3ac0a46fSAndroid Build Coastguard Worker .AsStringView()));
279*3ac0a46fSAndroid Build Coastguard Worker }
280*3ac0a46fSAndroid Build Coastguard Worker
281*3ac0a46fSAndroid Build Coastguard Worker // static
StringPrintx(const WideString & wsFormat,const WideString & wsSource)282*3ac0a46fSAndroid Build Coastguard Worker WideString CJS_Util::StringPrintx(const WideString& wsFormat,
283*3ac0a46fSAndroid Build Coastguard Worker const WideString& wsSource) {
284*3ac0a46fSAndroid Build Coastguard Worker WideString wsResult;
285*3ac0a46fSAndroid Build Coastguard Worker wsResult.Reserve(wsFormat.GetLength());
286*3ac0a46fSAndroid Build Coastguard Worker size_t iSourceIdx = 0;
287*3ac0a46fSAndroid Build Coastguard Worker size_t iFormatIdx = 0;
288*3ac0a46fSAndroid Build Coastguard Worker CaseMode eCaseMode = kPreserveCase;
289*3ac0a46fSAndroid Build Coastguard Worker bool bEscaped = false;
290*3ac0a46fSAndroid Build Coastguard Worker while (iFormatIdx < wsFormat.GetLength()) {
291*3ac0a46fSAndroid Build Coastguard Worker if (bEscaped) {
292*3ac0a46fSAndroid Build Coastguard Worker bEscaped = false;
293*3ac0a46fSAndroid Build Coastguard Worker wsResult += wsFormat[iFormatIdx];
294*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
295*3ac0a46fSAndroid Build Coastguard Worker continue;
296*3ac0a46fSAndroid Build Coastguard Worker }
297*3ac0a46fSAndroid Build Coastguard Worker switch (wsFormat[iFormatIdx]) {
298*3ac0a46fSAndroid Build Coastguard Worker case '\\': {
299*3ac0a46fSAndroid Build Coastguard Worker bEscaped = true;
300*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
301*3ac0a46fSAndroid Build Coastguard Worker } break;
302*3ac0a46fSAndroid Build Coastguard Worker case '<': {
303*3ac0a46fSAndroid Build Coastguard Worker eCaseMode = kLowerCase;
304*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
305*3ac0a46fSAndroid Build Coastguard Worker } break;
306*3ac0a46fSAndroid Build Coastguard Worker case '>': {
307*3ac0a46fSAndroid Build Coastguard Worker eCaseMode = kUpperCase;
308*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
309*3ac0a46fSAndroid Build Coastguard Worker } break;
310*3ac0a46fSAndroid Build Coastguard Worker case '=': {
311*3ac0a46fSAndroid Build Coastguard Worker eCaseMode = kPreserveCase;
312*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
313*3ac0a46fSAndroid Build Coastguard Worker } break;
314*3ac0a46fSAndroid Build Coastguard Worker case '?': {
315*3ac0a46fSAndroid Build Coastguard Worker if (iSourceIdx < wsSource.GetLength()) {
316*3ac0a46fSAndroid Build Coastguard Worker wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
317*3ac0a46fSAndroid Build Coastguard Worker ++iSourceIdx;
318*3ac0a46fSAndroid Build Coastguard Worker }
319*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
320*3ac0a46fSAndroid Build Coastguard Worker } break;
321*3ac0a46fSAndroid Build Coastguard Worker case 'X': {
322*3ac0a46fSAndroid Build Coastguard Worker if (iSourceIdx < wsSource.GetLength()) {
323*3ac0a46fSAndroid Build Coastguard Worker if (isascii(wsSource[iSourceIdx]) && isalnum(wsSource[iSourceIdx])) {
324*3ac0a46fSAndroid Build Coastguard Worker wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
325*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
326*3ac0a46fSAndroid Build Coastguard Worker }
327*3ac0a46fSAndroid Build Coastguard Worker ++iSourceIdx;
328*3ac0a46fSAndroid Build Coastguard Worker } else {
329*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
330*3ac0a46fSAndroid Build Coastguard Worker }
331*3ac0a46fSAndroid Build Coastguard Worker } break;
332*3ac0a46fSAndroid Build Coastguard Worker case 'A': {
333*3ac0a46fSAndroid Build Coastguard Worker if (iSourceIdx < wsSource.GetLength()) {
334*3ac0a46fSAndroid Build Coastguard Worker if (isascii(wsSource[iSourceIdx]) && isalpha(wsSource[iSourceIdx])) {
335*3ac0a46fSAndroid Build Coastguard Worker wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
336*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
337*3ac0a46fSAndroid Build Coastguard Worker }
338*3ac0a46fSAndroid Build Coastguard Worker ++iSourceIdx;
339*3ac0a46fSAndroid Build Coastguard Worker } else {
340*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
341*3ac0a46fSAndroid Build Coastguard Worker }
342*3ac0a46fSAndroid Build Coastguard Worker } break;
343*3ac0a46fSAndroid Build Coastguard Worker case '9': {
344*3ac0a46fSAndroid Build Coastguard Worker if (iSourceIdx < wsSource.GetLength()) {
345*3ac0a46fSAndroid Build Coastguard Worker if (FXSYS_IsDecimalDigit(wsSource[iSourceIdx])) {
346*3ac0a46fSAndroid Build Coastguard Worker wsResult += wsSource[iSourceIdx];
347*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
348*3ac0a46fSAndroid Build Coastguard Worker }
349*3ac0a46fSAndroid Build Coastguard Worker ++iSourceIdx;
350*3ac0a46fSAndroid Build Coastguard Worker } else {
351*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
352*3ac0a46fSAndroid Build Coastguard Worker }
353*3ac0a46fSAndroid Build Coastguard Worker } break;
354*3ac0a46fSAndroid Build Coastguard Worker case '*': {
355*3ac0a46fSAndroid Build Coastguard Worker if (iSourceIdx < wsSource.GetLength()) {
356*3ac0a46fSAndroid Build Coastguard Worker wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
357*3ac0a46fSAndroid Build Coastguard Worker ++iSourceIdx;
358*3ac0a46fSAndroid Build Coastguard Worker } else {
359*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
360*3ac0a46fSAndroid Build Coastguard Worker }
361*3ac0a46fSAndroid Build Coastguard Worker } break;
362*3ac0a46fSAndroid Build Coastguard Worker default: {
363*3ac0a46fSAndroid Build Coastguard Worker wsResult += wsFormat[iFormatIdx];
364*3ac0a46fSAndroid Build Coastguard Worker ++iFormatIdx;
365*3ac0a46fSAndroid Build Coastguard Worker } break;
366*3ac0a46fSAndroid Build Coastguard Worker }
367*3ac0a46fSAndroid Build Coastguard Worker }
368*3ac0a46fSAndroid Build Coastguard Worker return wsResult;
369*3ac0a46fSAndroid Build Coastguard Worker }
370*3ac0a46fSAndroid Build Coastguard Worker
scand(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)371*3ac0a46fSAndroid Build Coastguard Worker CJS_Result CJS_Util::scand(CJS_Runtime* pRuntime,
372*3ac0a46fSAndroid Build Coastguard Worker const std::vector<v8::Local<v8::Value>>& params) {
373*3ac0a46fSAndroid Build Coastguard Worker if (params.size() < 2)
374*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kParamError);
375*3ac0a46fSAndroid Build Coastguard Worker
376*3ac0a46fSAndroid Build Coastguard Worker WideString sFormat = pRuntime->ToWideString(params[0]);
377*3ac0a46fSAndroid Build Coastguard Worker WideString sDate = pRuntime->ToWideString(params[1]);
378*3ac0a46fSAndroid Build Coastguard Worker double dDate = FX_GetDateTime();
379*3ac0a46fSAndroid Build Coastguard Worker if (sDate.GetLength() > 0)
380*3ac0a46fSAndroid Build Coastguard Worker dDate = CJS_PublicMethods::ParseDateUsingFormat(pRuntime->GetIsolate(),
381*3ac0a46fSAndroid Build Coastguard Worker sDate, sFormat, nullptr);
382*3ac0a46fSAndroid Build Coastguard Worker if (isnan(dDate))
383*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Success(pRuntime->NewUndefined());
384*3ac0a46fSAndroid Build Coastguard Worker
385*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Success(pRuntime->NewDate(dDate));
386*3ac0a46fSAndroid Build Coastguard Worker }
387*3ac0a46fSAndroid Build Coastguard Worker
byteToChar(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)388*3ac0a46fSAndroid Build Coastguard Worker CJS_Result CJS_Util::byteToChar(
389*3ac0a46fSAndroid Build Coastguard Worker CJS_Runtime* pRuntime,
390*3ac0a46fSAndroid Build Coastguard Worker const std::vector<v8::Local<v8::Value>>& params) {
391*3ac0a46fSAndroid Build Coastguard Worker if (params.size() < 1)
392*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kParamError);
393*3ac0a46fSAndroid Build Coastguard Worker
394*3ac0a46fSAndroid Build Coastguard Worker int arg = pRuntime->ToInt32(params[0]);
395*3ac0a46fSAndroid Build Coastguard Worker if (arg < 0 || arg > 255)
396*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Failure(JSMessage::kValueError);
397*3ac0a46fSAndroid Build Coastguard Worker
398*3ac0a46fSAndroid Build Coastguard Worker WideString wStr(static_cast<wchar_t>(arg));
399*3ac0a46fSAndroid Build Coastguard Worker return CJS_Result::Success(pRuntime->NewString(wStr.AsStringView()));
400*3ac0a46fSAndroid Build Coastguard Worker }
401*3ac0a46fSAndroid Build Coastguard Worker
402*3ac0a46fSAndroid Build Coastguard Worker // static
ParseDataType(WideString * sFormat)403*3ac0a46fSAndroid Build Coastguard Worker CJS_Util::DataType CJS_Util::ParseDataType(WideString* sFormat) {
404*3ac0a46fSAndroid Build Coastguard Worker enum State { kBefore, kFlags, kWidth, kPrecision, kSpecifier, kAfter };
405*3ac0a46fSAndroid Build Coastguard Worker
406*3ac0a46fSAndroid Build Coastguard Worker DataType result = DataType::kInvalid;
407*3ac0a46fSAndroid Build Coastguard Worker State state = kBefore;
408*3ac0a46fSAndroid Build Coastguard Worker size_t precision_digits = 0;
409*3ac0a46fSAndroid Build Coastguard Worker size_t i = 0;
410*3ac0a46fSAndroid Build Coastguard Worker while (i < sFormat->GetLength()) {
411*3ac0a46fSAndroid Build Coastguard Worker wchar_t c = (*sFormat)[i];
412*3ac0a46fSAndroid Build Coastguard Worker switch (state) {
413*3ac0a46fSAndroid Build Coastguard Worker case kBefore:
414*3ac0a46fSAndroid Build Coastguard Worker if (c == L'%')
415*3ac0a46fSAndroid Build Coastguard Worker state = kFlags;
416*3ac0a46fSAndroid Build Coastguard Worker break;
417*3ac0a46fSAndroid Build Coastguard Worker case kFlags:
418*3ac0a46fSAndroid Build Coastguard Worker if (c == L'+' || c == L'-' || c == L'#' || c == L' ') {
419*3ac0a46fSAndroid Build Coastguard Worker // Stay in same state.
420*3ac0a46fSAndroid Build Coastguard Worker } else {
421*3ac0a46fSAndroid Build Coastguard Worker state = kWidth;
422*3ac0a46fSAndroid Build Coastguard Worker continue; // Re-process same character.
423*3ac0a46fSAndroid Build Coastguard Worker }
424*3ac0a46fSAndroid Build Coastguard Worker break;
425*3ac0a46fSAndroid Build Coastguard Worker case kWidth:
426*3ac0a46fSAndroid Build Coastguard Worker if (c == L'*')
427*3ac0a46fSAndroid Build Coastguard Worker return DataType::kInvalid;
428*3ac0a46fSAndroid Build Coastguard Worker if (FXSYS_IsDecimalDigit(c)) {
429*3ac0a46fSAndroid Build Coastguard Worker // Stay in same state.
430*3ac0a46fSAndroid Build Coastguard Worker } else if (c == L'.') {
431*3ac0a46fSAndroid Build Coastguard Worker state = kPrecision;
432*3ac0a46fSAndroid Build Coastguard Worker } else {
433*3ac0a46fSAndroid Build Coastguard Worker state = kSpecifier;
434*3ac0a46fSAndroid Build Coastguard Worker continue; // Re-process same character.
435*3ac0a46fSAndroid Build Coastguard Worker }
436*3ac0a46fSAndroid Build Coastguard Worker break;
437*3ac0a46fSAndroid Build Coastguard Worker case kPrecision:
438*3ac0a46fSAndroid Build Coastguard Worker if (c == L'*')
439*3ac0a46fSAndroid Build Coastguard Worker return DataType::kInvalid;
440*3ac0a46fSAndroid Build Coastguard Worker if (FXSYS_IsDecimalDigit(c)) {
441*3ac0a46fSAndroid Build Coastguard Worker // Stay in same state.
442*3ac0a46fSAndroid Build Coastguard Worker ++precision_digits;
443*3ac0a46fSAndroid Build Coastguard Worker } else {
444*3ac0a46fSAndroid Build Coastguard Worker state = kSpecifier;
445*3ac0a46fSAndroid Build Coastguard Worker continue; // Re-process same character.
446*3ac0a46fSAndroid Build Coastguard Worker }
447*3ac0a46fSAndroid Build Coastguard Worker break;
448*3ac0a46fSAndroid Build Coastguard Worker case kSpecifier:
449*3ac0a46fSAndroid Build Coastguard Worker if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' ||
450*3ac0a46fSAndroid Build Coastguard Worker c == L'u' || c == L'x' || c == L'X') {
451*3ac0a46fSAndroid Build Coastguard Worker result = DataType::kInt;
452*3ac0a46fSAndroid Build Coastguard Worker } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' ||
453*3ac0a46fSAndroid Build Coastguard Worker c == L'G') {
454*3ac0a46fSAndroid Build Coastguard Worker result = DataType::kDouble;
455*3ac0a46fSAndroid Build Coastguard Worker } else if (c == L's' || c == L'S') {
456*3ac0a46fSAndroid Build Coastguard Worker // Map s to S since we always deal internally with wchar_t strings.
457*3ac0a46fSAndroid Build Coastguard Worker // TODO(tsepez): Probably 100% borked. %S is not a standard
458*3ac0a46fSAndroid Build Coastguard Worker // conversion.
459*3ac0a46fSAndroid Build Coastguard Worker sFormat->SetAt(i, L'S');
460*3ac0a46fSAndroid Build Coastguard Worker result = DataType::kString;
461*3ac0a46fSAndroid Build Coastguard Worker } else {
462*3ac0a46fSAndroid Build Coastguard Worker return DataType::kInvalid;
463*3ac0a46fSAndroid Build Coastguard Worker }
464*3ac0a46fSAndroid Build Coastguard Worker state = kAfter;
465*3ac0a46fSAndroid Build Coastguard Worker break;
466*3ac0a46fSAndroid Build Coastguard Worker case kAfter:
467*3ac0a46fSAndroid Build Coastguard Worker if (c == L'%')
468*3ac0a46fSAndroid Build Coastguard Worker return DataType::kInvalid;
469*3ac0a46fSAndroid Build Coastguard Worker // Stay in same state until string exhausted.
470*3ac0a46fSAndroid Build Coastguard Worker break;
471*3ac0a46fSAndroid Build Coastguard Worker }
472*3ac0a46fSAndroid Build Coastguard Worker ++i;
473*3ac0a46fSAndroid Build Coastguard Worker }
474*3ac0a46fSAndroid Build Coastguard Worker // See https://crbug.com/740166
475*3ac0a46fSAndroid Build Coastguard Worker if (result == DataType::kInt && precision_digits > 2)
476*3ac0a46fSAndroid Build Coastguard Worker return DataType::kInvalid;
477*3ac0a46fSAndroid Build Coastguard Worker
478*3ac0a46fSAndroid Build Coastguard Worker return result;
479*3ac0a46fSAndroid Build Coastguard Worker }
480