xref: /aosp_15_r20/external/pdfium/fxjs/cjs_util.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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