blob: b82020d874beb7752045924e855fbed9a9d18279 [file] [log] [blame]
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001// Copyright 2014 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
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 Sinclairf766ad22016-03-14 13:51:24 -04007#include "fpdfsdk/javascript/util.h"
Tom Sepez37458412015-10-06 11:33:46 -07008
Lei Zhang6b37a5f2015-12-25 00:20:59 -08009#include <time.h>
10
tsepez86a61dc2016-03-25 10:00:11 -070011#include <algorithm>
Tom Sepezdf950b82017-08-04 11:33:49 -070012#include <cmath>
Lei Zhange247ec42017-04-20 21:41:36 -070013#include <cwctype>
Dan Sinclair3ebd1212016-03-09 09:59:23 -050014#include <string>
15#include <vector>
16
Dan Sinclaircfb19442017-04-20 13:13:04 -040017#include "core/fxcrt/fx_extension.h"
Dan Sinclairf766ad22016-03-14 13:51:24 -040018#include "fpdfsdk/javascript/JS_Define.h"
19#include "fpdfsdk/javascript/JS_EventHandler.h"
20#include "fpdfsdk/javascript/JS_Object.h"
Dan Sinclairf766ad22016-03-14 13:51:24 -040021#include "fpdfsdk/javascript/JS_Value.h"
22#include "fpdfsdk/javascript/PublicMethods.h"
Tom Sepezd6ae2af2017-02-16 11:49:55 -080023#include "fpdfsdk/javascript/cjs_event_context.h"
dsinclair64376be2016-03-31 20:03:24 -070024#include "fpdfsdk/javascript/cjs_runtime.h"
Dan Sinclairf766ad22016-03-14 13:51:24 -040025#include "fpdfsdk/javascript/resource.h"
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070026
Dan Sinclair698aed72017-09-26 16:24:49 -040027#if _FX_OS_ == _FX_OS_ANDROID_
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070028#include <ctype.h>
29#endif
30
Tom Sepez04557b82017-02-16 09:43:10 -080031JSConstSpec CJS_Util::ConstSpecs[] = {{0, JSConstSpec::Number, 0, 0}};
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070032
Tom Sepez04557b82017-02-16 09:43:10 -080033JSPropertySpec CJS_Util::PropertySpecs[] = {{0, 0, 0}};
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070034
Tom Sepez04557b82017-02-16 09:43:10 -080035JSMethodSpec CJS_Util::MethodSpecs[] = {
Tom Sepez9b99b632017-02-21 15:05:57 -080036 {"printd", printd_static}, {"printf", printf_static},
37 {"printx", printx_static}, {"scand", scand_static},
38 {"byteToChar", byteToChar_static}, {0, 0}};
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070039
Nico Weber9d8ec5a2015-08-04 13:00:21 -070040IMPLEMENT_JS_CLASS(CJS_Util, util)
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070041
tsepez86a61dc2016-03-25 10:00:11 -070042namespace {
43
44// Map PDF-style directives to equivalent wcsftime directives. Not
45// all have direct equivalents, though.
46struct TbConvert {
Dan Sinclair812e96c2017-03-13 16:43:37 -040047 const wchar_t* lpszJSMark;
48 const wchar_t* lpszCppMark;
tsepez86a61dc2016-03-25 10:00:11 -070049};
50
51// Map PDF-style directives lacking direct wcsftime directives to
52// the value with which they will be replaced.
53struct TbConvertAdditional {
Dan Sinclair812e96c2017-03-13 16:43:37 -040054 const wchar_t* lpszJSMark;
tsepez86a61dc2016-03-25 10:00:11 -070055 int iValue;
56};
57
58const TbConvert TbConvertTable[] = {
59 {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"},
60 {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"},
61 {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"},
62 {L"TT", L"%p"},
63#if defined(_WIN32)
64 {L"tt", L"%p"}, {L"h", L"%#I"},
65#else
66 {L"tt", L"%P"}, {L"h", L"%l"},
67#endif
68};
69
tsepez86a61dc2016-03-25 10:00:11 -070070} // namespace
71
72util::util(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
73
74util::~util() {}
75
Tom Sepezb1670b52017-02-16 17:01:00 -080076bool util::printf(CJS_Runtime* pRuntime,
tsepez4cf55152016-11-02 14:37:54 -070077 const std::vector<CJS_Value>& params,
78 CJS_Value& vRet,
Ryan Harrison275e2602017-09-18 14:23:18 -040079 WideString& sError) {
Lei Zhang574b5742017-03-30 12:41:55 -070080 const size_t iSize = params.size();
Nico Weber9d8ec5a2015-08-04 13:00:21 -070081 if (iSize < 1)
tsepez4cf55152016-11-02 14:37:54 -070082 return false;
Lei Zhang574b5742017-03-30 12:41:55 -070083
Tom Sepezffbc0d92017-07-17 09:29:05 -070084 std::wstring unsafe_fmt_string(params[0].ToCFXWideString(pRuntime).c_str());
85 std::vector<std::wstring> unsafe_conversion_specifiers;
Nico Weber9d8ec5a2015-08-04 13:00:21 -070086 int iOffset = 0;
87 int iOffend = 0;
Tom Sepezffbc0d92017-07-17 09:29:05 -070088 unsafe_fmt_string.insert(unsafe_fmt_string.begin(), L'S');
Nico Weber9d8ec5a2015-08-04 13:00:21 -070089 while (iOffset != -1) {
Tom Sepezffbc0d92017-07-17 09:29:05 -070090 iOffend = unsafe_fmt_string.find(L"%", iOffset + 1);
Nico Weber9d8ec5a2015-08-04 13:00:21 -070091 std::wstring strSub;
92 if (iOffend == -1)
Tom Sepezffbc0d92017-07-17 09:29:05 -070093 strSub = unsafe_fmt_string.substr(iOffset);
Nico Weber9d8ec5a2015-08-04 13:00:21 -070094 else
Tom Sepezffbc0d92017-07-17 09:29:05 -070095 strSub = unsafe_fmt_string.substr(iOffset, iOffend - iOffset);
96 unsafe_conversion_specifiers.push_back(strSub);
Nico Weber9d8ec5a2015-08-04 13:00:21 -070097 iOffset = iOffend;
98 }
99
100 std::wstring c_strResult;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700101 for (size_t iIndex = 0; iIndex < unsafe_conversion_specifiers.size();
102 ++iIndex) {
103 std::wstring c_strFormat = unsafe_conversion_specifiers[iIndex];
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700104 if (iIndex == 0) {
105 c_strResult = c_strFormat;
106 continue;
107 }
108
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700109 if (iIndex >= iSize) {
110 c_strResult += c_strFormat;
111 continue;
112 }
113
Ryan Harrison275e2602017-09-18 14:23:18 -0400114 WideString strSegment;
tsepez86a61dc2016-03-25 10:00:11 -0700115 switch (ParseDataType(&c_strFormat)) {
Tom Sepezffbc0d92017-07-17 09:29:05 -0700116 case UTIL_INT:
tsepezb4694242016-08-15 16:44:55 -0700117 strSegment.Format(c_strFormat.c_str(), params[iIndex].ToInt(pRuntime));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700118 break;
119 case UTIL_DOUBLE:
tsepezf3dc8c62016-08-10 06:29:29 -0700120 strSegment.Format(c_strFormat.c_str(),
tsepezb4694242016-08-15 16:44:55 -0700121 params[iIndex].ToDouble(pRuntime));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700122 break;
123 case UTIL_STRING:
tsepezb4694242016-08-15 16:44:55 -0700124 strSegment.Format(c_strFormat.c_str(),
125 params[iIndex].ToCFXWideString(pRuntime).c_str());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700126 break;
127 default:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700128 strSegment.Format(L"%ls", c_strFormat.c_str());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700129 break;
130 }
Lei Zhang1e25e122017-06-16 02:14:40 -0700131 c_strResult += strSegment.c_str();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700132 }
133
134 c_strResult.erase(c_strResult.begin());
tsepezf3dc8c62016-08-10 06:29:29 -0700135 vRet = CJS_Value(pRuntime, c_strResult.c_str());
tsepez4cf55152016-11-02 14:37:54 -0700136 return true;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700137}
138
Tom Sepezb1670b52017-02-16 17:01:00 -0800139bool util::printd(CJS_Runtime* pRuntime,
tsepez4cf55152016-11-02 14:37:54 -0700140 const std::vector<CJS_Value>& params,
141 CJS_Value& vRet,
Ryan Harrison275e2602017-09-18 14:23:18 -0400142 WideString& sError) {
Lei Zhang574b5742017-03-30 12:41:55 -0700143 const size_t iSize = params.size();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700144 if (iSize < 2)
tsepez4cf55152016-11-02 14:37:54 -0700145 return false;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700146
Lei Zhang574b5742017-03-30 12:41:55 -0700147 const CJS_Value& p1 = params[0];
148 const CJS_Value& p2 = params[1];
tsepezf3c88322016-08-09 07:30:38 -0700149 CJS_Date jsDate;
tsepezb4694242016-08-15 16:44:55 -0700150 if (!p2.ConvertToDate(pRuntime, jsDate)) {
tsepezcd5dc852016-09-08 11:23:24 -0700151 sError = JSGetStringFromID(IDS_STRING_JSPRINT1);
tsepez4cf55152016-11-02 14:37:54 -0700152 return false;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700153 }
154
tsepezb4694242016-08-15 16:44:55 -0700155 if (!jsDate.IsValidDate(pRuntime)) {
tsepezcd5dc852016-09-08 11:23:24 -0700156 sError = JSGetStringFromID(IDS_STRING_JSPRINT2);
tsepez4cf55152016-11-02 14:37:54 -0700157 return false;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700158 }
159
Tom Sepez39bfe122015-09-17 15:25:23 -0700160 if (p1.GetType() == CJS_Value::VT_number) {
Ryan Harrison275e2602017-09-18 14:23:18 -0400161 WideString swResult;
tsepezb4694242016-08-15 16:44:55 -0700162 switch (p1.ToInt(pRuntime)) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700163 case 0:
tsepezb4694242016-08-15 16:44:55 -0700164 swResult.Format(L"D:%04d%02d%02d%02d%02d%02d", jsDate.GetYear(pRuntime),
165 jsDate.GetMonth(pRuntime) + 1, jsDate.GetDay(pRuntime),
166 jsDate.GetHours(pRuntime), jsDate.GetMinutes(pRuntime),
167 jsDate.GetSeconds(pRuntime));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700168 break;
169 case 1:
tsepezf3c88322016-08-09 07:30:38 -0700170 swResult.Format(L"%04d.%02d.%02d %02d:%02d:%02d",
tsepezb4694242016-08-15 16:44:55 -0700171 jsDate.GetYear(pRuntime), jsDate.GetMonth(pRuntime) + 1,
172 jsDate.GetDay(pRuntime), jsDate.GetHours(pRuntime),
173 jsDate.GetMinutes(pRuntime),
174 jsDate.GetSeconds(pRuntime));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700175 break;
176 case 2:
tsepezf3c88322016-08-09 07:30:38 -0700177 swResult.Format(L"%04d/%02d/%02d %02d:%02d:%02d",
tsepezb4694242016-08-15 16:44:55 -0700178 jsDate.GetYear(pRuntime), jsDate.GetMonth(pRuntime) + 1,
179 jsDate.GetDay(pRuntime), jsDate.GetHours(pRuntime),
180 jsDate.GetMinutes(pRuntime),
181 jsDate.GetSeconds(pRuntime));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700182 break;
183 default:
tsepezcd5dc852016-09-08 11:23:24 -0700184 sError = JSGetStringFromID(IDS_STRING_JSVALUEERROR);
tsepez4cf55152016-11-02 14:37:54 -0700185 return false;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700186 }
187
tsepezf3dc8c62016-08-10 06:29:29 -0700188 vRet = CJS_Value(pRuntime, swResult.c_str());
tsepez4cf55152016-11-02 14:37:54 -0700189 return true;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700190 }
tsepez86a61dc2016-03-25 10:00:11 -0700191
Tom Sepez39bfe122015-09-17 15:25:23 -0700192 if (p1.GetType() == CJS_Value::VT_string) {
tsepezb4694242016-08-15 16:44:55 -0700193 if (iSize > 2 && params[2].ToBool(pRuntime)) {
tsepezcd5dc852016-09-08 11:23:24 -0700194 sError = JSGetStringFromID(IDS_STRING_JSNOTSUPPORT);
tsepez4cf55152016-11-02 14:37:54 -0700195 return false; // currently, it doesn't support XFAPicture.
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700196 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700197
tsepez86a61dc2016-03-25 10:00:11 -0700198 // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
199 // pre-existing %-directives before inserting our own.
tsepezb4694242016-08-15 16:44:55 -0700200 std::basic_string<wchar_t> cFormat = p1.ToCFXWideString(pRuntime).c_str();
tsepez86a61dc2016-03-25 10:00:11 -0700201 cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
202 cFormat.end());
203
204 for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700205 int iStart = 0;
206 int iEnd;
tsepez86a61dc2016-03-25 10:00:11 -0700207 while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) !=
208 -1) {
Ryan Harrison8b1408e2017-09-27 11:07:51 -0400209 cFormat.replace(iEnd, wcslen(TbConvertTable[i].lpszJSMark),
tsepez86a61dc2016-03-25 10:00:11 -0700210 TbConvertTable[i].lpszCppMark);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700211 iStart = iEnd;
212 }
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700213 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700214
tsepezb4694242016-08-15 16:44:55 -0700215 int iYear = jsDate.GetYear(pRuntime);
Lei Zhang2bf942d2017-06-16 13:48:19 -0700216 if (iYear < 0) {
217 sError = JSGetStringFromID(IDS_STRING_JSVALUEERROR);
218 return false;
219 }
220
tsepezb4694242016-08-15 16:44:55 -0700221 int iMonth = jsDate.GetMonth(pRuntime);
222 int iDay = jsDate.GetDay(pRuntime);
223 int iHour = jsDate.GetHours(pRuntime);
224 int iMin = jsDate.GetMinutes(pRuntime);
225 int iSec = jsDate.GetSeconds(pRuntime);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700226
Lei Zhang1e25e122017-06-16 02:14:40 -0700227 static const TbConvertAdditional cTableAd[] = {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700228 {L"m", iMonth + 1}, {L"d", iDay},
229 {L"H", iHour}, {L"h", iHour > 12 ? iHour - 12 : iHour},
230 {L"M", iMin}, {L"s", iSec},
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700231 };
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700232
tsepez86a61dc2016-03-25 10:00:11 -0700233 for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) {
Ryan Harrison275e2602017-09-18 14:23:18 -0400234 WideString sValue;
Lei Zhangb9c31972015-08-11 14:09:35 -0700235 sValue.Format(L"%d", cTableAd[i].iValue);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700236
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700237 int iStart = 0;
238 int iEnd;
Lei Zhangb9c31972015-08-11 14:09:35 -0700239 while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700240 if (iEnd > 0) {
241 if (cFormat[iEnd - 1] == L'%') {
242 iStart = iEnd + 1;
243 continue;
244 }
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700245 }
Ryan Harrison8b1408e2017-09-27 11:07:51 -0400246 cFormat.replace(iEnd, wcslen(cTableAd[i].lpszJSMark), sValue.c_str());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700247 iStart = iEnd;
248 }
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700249 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700250
tsepez86a61dc2016-03-25 10:00:11 -0700251 struct tm time = {};
252 time.tm_year = iYear - 1900;
253 time.tm_mon = iMonth;
254 time.tm_mday = iDay;
255 time.tm_hour = iHour;
256 time.tm_min = iMin;
257 time.tm_sec = iSec;
258
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700259 wchar_t buf[64] = {};
Lei Zhang2bf942d2017-06-16 13:48:19 -0700260 FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time);
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700261 cFormat = buf;
tsepezf3dc8c62016-08-10 06:29:29 -0700262 vRet = CJS_Value(pRuntime, cFormat.c_str());
tsepez4cf55152016-11-02 14:37:54 -0700263 return true;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700264 }
tsepez86a61dc2016-03-25 10:00:11 -0700265
tsepezcd5dc852016-09-08 11:23:24 -0700266 sError = JSGetStringFromID(IDS_STRING_JSTYPEERROR);
tsepez4cf55152016-11-02 14:37:54 -0700267 return false;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700268}
269
Tom Sepezb1670b52017-02-16 17:01:00 -0800270bool util::printx(CJS_Runtime* pRuntime,
tsepez4cf55152016-11-02 14:37:54 -0700271 const std::vector<CJS_Value>& params,
272 CJS_Value& vRet,
Ryan Harrison275e2602017-09-18 14:23:18 -0400273 WideString& sError) {
tsepez4f1f41f2016-03-28 14:13:16 -0700274 if (params.size() < 2) {
tsepezcd5dc852016-09-08 11:23:24 -0700275 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR);
tsepez4cf55152016-11-02 14:37:54 -0700276 return false;
tsepez4f1f41f2016-03-28 14:13:16 -0700277 }
tsepezf3dc8c62016-08-10 06:29:29 -0700278
tsepezb4694242016-08-15 16:44:55 -0700279 vRet = CJS_Value(pRuntime, printx(params[0].ToCFXWideString(pRuntime),
280 params[1].ToCFXWideString(pRuntime))
281 .c_str());
tsepezf3dc8c62016-08-10 06:29:29 -0700282
tsepez4cf55152016-11-02 14:37:54 -0700283 return true;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700284}
285
tsepez4f1f41f2016-03-28 14:13:16 -0700286enum CaseMode { kPreserveCase, kUpperCase, kLowerCase };
287
Dan Sinclair812e96c2017-03-13 16:43:37 -0400288static wchar_t TranslateCase(wchar_t input, CaseMode eMode) {
Lei Zhangef002c82017-04-24 16:28:07 -0700289 if (eMode == kLowerCase && FXSYS_isupper(input))
tsepez4f1f41f2016-03-28 14:13:16 -0700290 return input | 0x20;
Lei Zhangef002c82017-04-24 16:28:07 -0700291 if (eMode == kUpperCase && FXSYS_islower(input))
tsepez4f1f41f2016-03-28 14:13:16 -0700292 return input & ~0x20;
293 return input;
294}
295
Ryan Harrison275e2602017-09-18 14:23:18 -0400296WideString util::printx(const WideString& wsFormat,
297 const WideString& wsSource) {
298 WideString wsResult;
Ryan Harrison875e98c2017-09-27 10:53:11 -0400299 size_t iSourceIdx = 0;
300 size_t iFormatIdx = 0;
tsepez4f1f41f2016-03-28 14:13:16 -0700301 CaseMode eCaseMode = kPreserveCase;
302 bool bEscaped = false;
303 while (iFormatIdx < wsFormat.GetLength()) {
304 if (bEscaped) {
305 bEscaped = false;
306 wsResult += wsFormat[iFormatIdx];
307 ++iFormatIdx;
308 continue;
309 }
310 switch (wsFormat[iFormatIdx]) {
311 case '\\': {
312 bEscaped = true;
313 ++iFormatIdx;
314 } break;
315 case '<': {
316 eCaseMode = kLowerCase;
317 ++iFormatIdx;
318 } break;
319 case '>': {
320 eCaseMode = kUpperCase;
321 ++iFormatIdx;
322 } break;
323 case '=': {
324 eCaseMode = kPreserveCase;
325 ++iFormatIdx;
326 } break;
327 case '?': {
328 if (iSourceIdx < wsSource.GetLength()) {
329 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
330 ++iSourceIdx;
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700331 }
tsepez4f1f41f2016-03-28 14:13:16 -0700332 ++iFormatIdx;
333 } break;
334 case 'X': {
335 if (iSourceIdx < wsSource.GetLength()) {
Lei Zhangef002c82017-04-24 16:28:07 -0700336 if (FXSYS_iswalnum(wsSource[iSourceIdx])) {
tsepez4f1f41f2016-03-28 14:13:16 -0700337 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
338 ++iFormatIdx;
339 }
340 ++iSourceIdx;
341 } else {
342 ++iFormatIdx;
343 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700344 } break;
345 case 'A': {
tsepez4f1f41f2016-03-28 14:13:16 -0700346 if (iSourceIdx < wsSource.GetLength()) {
Lei Zhangef002c82017-04-24 16:28:07 -0700347 if (FXSYS_iswalpha(wsSource[iSourceIdx])) {
tsepez4f1f41f2016-03-28 14:13:16 -0700348 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
349 ++iFormatIdx;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700350 }
tsepez4f1f41f2016-03-28 14:13:16 -0700351 ++iSourceIdx;
352 } else {
353 ++iFormatIdx;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700354 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700355 } break;
356 case '9': {
tsepez4f1f41f2016-03-28 14:13:16 -0700357 if (iSourceIdx < wsSource.GetLength()) {
Lei Zhangef002c82017-04-24 16:28:07 -0700358 if (std::iswdigit(wsSource[iSourceIdx])) {
tsepez4f1f41f2016-03-28 14:13:16 -0700359 wsResult += wsSource[iSourceIdx];
360 ++iFormatIdx;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700361 }
tsepez4f1f41f2016-03-28 14:13:16 -0700362 ++iSourceIdx;
363 } else {
364 ++iFormatIdx;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700365 }
tsepez4f1f41f2016-03-28 14:13:16 -0700366 } break;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700367 case '*': {
tsepez4f1f41f2016-03-28 14:13:16 -0700368 if (iSourceIdx < wsSource.GetLength()) {
369 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
370 ++iSourceIdx;
371 } else {
372 ++iFormatIdx;
373 }
374 } break;
375 default: {
376 wsResult += wsFormat[iFormatIdx];
377 ++iFormatIdx;
378 } break;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700379 }
380 }
tsepez4f1f41f2016-03-28 14:13:16 -0700381 return wsResult;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700382}
383
Tom Sepezb1670b52017-02-16 17:01:00 -0800384bool util::scand(CJS_Runtime* pRuntime,
tsepez4cf55152016-11-02 14:37:54 -0700385 const std::vector<CJS_Value>& params,
386 CJS_Value& vRet,
Ryan Harrison275e2602017-09-18 14:23:18 -0400387 WideString& sError) {
Lei Zhang574b5742017-03-30 12:41:55 -0700388 if (params.size() < 2)
tsepez4cf55152016-11-02 14:37:54 -0700389 return false;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700390
Ryan Harrison275e2602017-09-18 14:23:18 -0400391 WideString sFormat = params[0].ToCFXWideString(pRuntime);
392 WideString sDate = params[1].ToCFXWideString(pRuntime);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700393 double dDate = JS_GetDateTime();
394 if (sDate.GetLength() > 0) {
Lei Zhang9559b7a2015-12-21 11:12:20 -0800395 dDate = CJS_PublicMethods::MakeRegularDate(sDate, sFormat, nullptr);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700396 }
397
Tom Sepezdf950b82017-08-04 11:33:49 -0700398 if (!std::isnan(dDate)) {
tsepezb4694242016-08-15 16:44:55 -0700399 vRet = CJS_Value(pRuntime, CJS_Date(pRuntime, dDate));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700400 } else {
tsepezf3dc8c62016-08-10 06:29:29 -0700401 vRet.SetNull(pRuntime);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700402 }
403
tsepez4cf55152016-11-02 14:37:54 -0700404 return true;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700405}
406
Tom Sepezb1670b52017-02-16 17:01:00 -0800407bool util::byteToChar(CJS_Runtime* pRuntime,
tsepez4cf55152016-11-02 14:37:54 -0700408 const std::vector<CJS_Value>& params,
409 CJS_Value& vRet,
Ryan Harrison275e2602017-09-18 14:23:18 -0400410 WideString& sError) {
tsepez90d87792016-03-29 09:21:54 -0700411 if (params.size() < 1) {
tsepezcd5dc852016-09-08 11:23:24 -0700412 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR);
tsepez4cf55152016-11-02 14:37:54 -0700413 return false;
tsepez90d87792016-03-29 09:21:54 -0700414 }
tsepezf3dc8c62016-08-10 06:29:29 -0700415
tsepezb4694242016-08-15 16:44:55 -0700416 int arg = params[0].ToInt(pRuntime);
tsepez90d87792016-03-29 09:21:54 -0700417 if (arg < 0 || arg > 255) {
tsepezcd5dc852016-09-08 11:23:24 -0700418 sError = JSGetStringFromID(IDS_STRING_JSVALUEERROR);
tsepez4cf55152016-11-02 14:37:54 -0700419 return false;
tsepez90d87792016-03-29 09:21:54 -0700420 }
tsepezf3dc8c62016-08-10 06:29:29 -0700421
Ryan Harrison275e2602017-09-18 14:23:18 -0400422 WideString wStr(static_cast<wchar_t>(arg));
tsepezf3dc8c62016-08-10 06:29:29 -0700423 vRet = CJS_Value(pRuntime, wStr.c_str());
tsepez4cf55152016-11-02 14:37:54 -0700424 return true;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700425}
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400426
Tom Sepezffbc0d92017-07-17 09:29:05 -0700427// Ensure that sFormat contains at most one well-understood printf formatting
428// directive which is safe to use with a single argument, and return the type
429// of argument expected, or -1 otherwise. If -1 is returned, it is NOT safe
430// to use sFormat with printf() and it must be copied byte-by-byte.
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400431int util::ParseDataType(std::wstring* sFormat) {
Tom Sepezffbc0d92017-07-17 09:29:05 -0700432 enum State { BEFORE, FLAGS, WIDTH, PRECISION, SPECIFIER, AFTER };
433
434 int result = -1;
435 State state = BEFORE;
436 size_t precision_digits = 0;
437 size_t i = 0;
438 while (i < sFormat->length()) {
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400439 wchar_t c = (*sFormat)[i];
Tom Sepezffbc0d92017-07-17 09:29:05 -0700440 switch (state) {
441 case BEFORE:
442 if (c == L'%')
443 state = FLAGS;
444 break;
445 case FLAGS:
446 if (c == L'+' || c == L'-' || c == L'#' || c == L' ') {
447 // Stay in same state.
448 } else {
449 state = WIDTH;
450 continue; // Re-process same character.
451 }
452 break;
453 case WIDTH:
454 if (c == L'*')
455 return -1;
456 if (std::iswdigit(c)) {
457 // Stay in same state.
458 } else if (c == L'.') {
459 state = PRECISION;
460 } else {
461 state = SPECIFIER;
462 continue; // Re-process same character.
463 }
464 break;
465 case PRECISION:
466 if (c == L'*')
467 return -1;
468 if (std::iswdigit(c)) {
469 // Stay in same state.
470 ++precision_digits;
471 } else {
472 state = SPECIFIER;
473 continue; // Re-process same character.
474 }
475 break;
476 case SPECIFIER:
477 if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' ||
478 c == L'u' || c == L'x' || c == L'X') {
479 result = UTIL_INT;
480 } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' ||
481 c == L'G') {
482 result = UTIL_DOUBLE;
483 } else if (c == L's' || c == L'S') {
484 // Map s to S since we always deal internally with wchar_t strings.
485 // TODO(tsepez): Probably 100% borked. %S is not a standard
486 // conversion.
487 (*sFormat)[i] = L'S';
488 result = UTIL_STRING;
489 } else {
490 return -1;
491 }
492 state = AFTER;
493 break;
494 case AFTER:
495 if (c == L'%')
496 return -1;
497 // Stay in same state until string exhausted.
498 break;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400499 }
Tom Sepezffbc0d92017-07-17 09:29:05 -0700500 ++i;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400501 }
Tom Sepezffbc0d92017-07-17 09:29:05 -0700502 // See https://crbug.com/740166
503 if (result == UTIL_INT && precision_digits > 2)
504 return -1;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400505
Tom Sepezffbc0d92017-07-17 09:29:05 -0700506 return result;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400507}