1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "utils/string_utils.h"
17 #include "utils/utils.h"
18 #include <cctype>
19 #include <cstdarg>
20 #include <cstdint>
21 #include <limits>
22 #include <vector>
23 #include <regex>
24 #include <algorithm>
25 #include "hilog_wrapper.h"
26 
27 #ifdef SUPPORT_GRAPHICS
28 #include "unicode/numberformatter.h"
29 #endif
30 
31 #if defined(__WINNT__)
32 #include <cstring>
33 #else
34 #include "securec.h"
35 #endif
36 
37 namespace OHOS {
38 namespace Global {
39 namespace Resource {
40 const std::regex PLACEHOLDER_MATCHING_RULES(R"((%%)|%((\d+)\$){0,1}(\.\d)?([dsf]))");
41 // the whole string match the regex, such as "%1$.2f" in the string "count is %1$.2f"
42 constexpr uint8_t MATCHE_INDEX_WHOLE_STRING = 0;
43 // match %%
44 constexpr uint8_t MATCHE_INDEX_DOUBLE_PERCENT = 1;
45 // match the placeholder index number, such as 1 in 1$
46 constexpr uint8_t MATCHE_INDEX_PLACEHOLDER_INDEX = 3;
47 // match precision, such as .2f
48 constexpr uint8_t MATCHE_INDEX_PRECISION = 4;
49 // match type [dsf]
50 constexpr uint8_t MATCHE_INDEX_PLACEHOLDER_TYPE = 5;
51 constexpr int32_t INVALID_PRECISION = -1;
52 const std::string SIZE_T_MAX_STR = std::to_string(std::numeric_limits<size_t>::max());
53 #ifdef SUPPORT_GRAPHICS
54 #endif
55 
FormatString(const char * fmt,...)56 std::string FormatString(const char *fmt, ...)
57 {
58     std::string strResult;
59     if (fmt != nullptr) {
60         va_list marker;
61         va_start(marker, fmt);
62         strResult = FormatString(fmt, marker);
63         va_end(marker);
64     }
65     return strResult;
66 }
67 
FormatString(const char * fmt,va_list args)68 std::string FormatString(const char *fmt, va_list args)
69 {
70     std::string strResult;
71     if (fmt != nullptr) {
72         va_list tmpArgs;
73         va_copy(tmpArgs, args);
74         int nLength = vsnprintf(nullptr, 0, fmt, tmpArgs); // compute buffer size
75         va_end(tmpArgs);
76         std::vector<char> vBuffer(nLength + 1, '\0');
77         int nWritten = vsnprintf_s(&vBuffer[0], nLength + 1, nLength, fmt, args);
78         if (nWritten > 0) {
79             strResult = &vBuffer[0];
80         }
81     }
82     return strResult;
83 }
84 
getJsParams(const std::string & inputOutputValue,va_list args,std::vector<std::pair<int,std::string>> paramsWithOutNum,std::vector<std::pair<int,std::string>> paramsWithNum,std::vector<std::tuple<ResourceManager::NapiValueType,std::string>> & jsParams)85 bool getJsParams(const std::string &inputOutputValue, va_list args,
86     std::vector<std::pair<int, std::string>> paramsWithOutNum,
87     std::vector<std::pair<int, std::string>> paramsWithNum,
88     std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
89 {
90     std::sort(paramsWithNum.begin(), paramsWithNum.end(),
91         [](const std::pair<int, std::string>& a, const std::pair<int, std::string>& b) {
92             return a.first < b.first;
93         });
94     for (size_t i = 0; i < paramsWithOutNum.size(); i++) {
95         std::string type = paramsWithOutNum[i].second;
96         if (type == "d") {
97             int temp = va_arg(args, int);
98             jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
99         } else if (type == "s") {
100             char *temp = va_arg(args, char*);
101             jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_STRING, temp);
102         } else if (type == "f") {
103             float temp = va_arg(args, double);
104             jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
105         }
106     }
107     for (size_t i = 0; i < paramsWithNum.size(); i++) {
108         size_t index = static_cast<size_t>(paramsWithNum[i].first);
109         std::string type = paramsWithNum[i].second;
110         if (index < paramsWithOutNum.size()) {
111             if (type != paramsWithOutNum[index].second) {
112                 return false;
113             }
114         } else if (index == paramsWithOutNum.size()) {
115             paramsWithOutNum.push_back({index, type});
116             if (type == "d") {
117                 int temp = va_arg(args, int);
118                 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
119             } else if (type == "s") {
120                 char *temp = va_arg(args, char*);
121                 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_STRING, temp);
122             } else if (type == "f") {
123                 float temp = va_arg(args, double);
124                 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
125             }
126         } else {
127             return false;
128         }
129     }
130     return true;
131 }
132 
parseArgs(const std::string & inputOutputValue,va_list args,std::vector<std::tuple<ResourceManager::NapiValueType,std::string>> & jsParams)133 bool parseArgs(const std::string &inputOutputValue, va_list args,
134     std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
135 {
136     if (inputOutputValue.empty()) {
137         return true;
138     }
139     std::string::const_iterator start = inputOutputValue.begin();
140     std::string::const_iterator end = inputOutputValue.end();
141     std::smatch matches;
142     size_t matchCount = 0;
143     int prefixLength = 0;
144     int offset = 2;
145     std::vector<std::pair<int, std::string>> paramsWithOutNum;
146     std::vector<std::pair<int, std::string>> paramsWithNum;
147     while (std::regex_search(start, end, matches, PLACEHOLDER_MATCHING_RULES)) {
148         prefixLength = matches[MATCHE_INDEX_WHOLE_STRING].first - inputOutputValue.begin();
149         if (matches[1].length() != 0) {
150             start = inputOutputValue.begin() + prefixLength + offset;
151             continue;
152         }
153         std::string placeholderIndex = matches[MATCHE_INDEX_PLACEHOLDER_INDEX];
154         std::string placeholderType = matches[MATCHE_INDEX_PLACEHOLDER_TYPE];
155         size_t paramIndex;
156         if (placeholderIndex.length() != 0) {
157             if (placeholderIndex.size() > SIZE_T_MAX_STR.size() ||
158                 (placeholderIndex.size() == SIZE_T_MAX_STR.size() && placeholderIndex > SIZE_T_MAX_STR)) {
159                 RESMGR_HILOGE(RESMGR_TAG, "index of placeholder is too large");
160                 return false;
161             }
162             unsigned long index;
163             if (!Utils::convertToUnsignedLong(placeholderIndex, index) || index < 1) {
164                 RESMGR_HILOGE(RESMGR_TAG, "convert value error, placeholderIndex = %{public}s",
165                     placeholderIndex.c_str());
166                 return false;
167             }
168             paramIndex = index - 1;
169             paramsWithNum.push_back({paramIndex, placeholderType});
170         } else {
171             paramIndex = matchCount++;
172             paramsWithOutNum.push_back({paramIndex, placeholderType});
173         }
174         start = inputOutputValue.begin() + prefixLength + matches[0].length();
175     }
176     return getJsParams(inputOutputValue, args, paramsWithOutNum, paramsWithNum, jsParams);
177 }
178 
GetLocalInfo(const ResLocale * resLocale)179 std::string GetLocalInfo(const ResLocale *resLocale)
180 {
181     std::string localeInfo;
182     const char *language = resLocale->GetLanguage();
183     if (language != nullptr && strlen(language) > 0) {
184         localeInfo.assign(language);
185     } else {
186         RESMGR_HILOGW(RESMGR_TAG, "GetLocalInfo language is null");
187         return localeInfo;
188     }
189     std::string temp;
190     const char *script = resLocale->GetScript();
191     if (script != nullptr && strlen(script) > 0) {
192         temp.assign(script);
193         localeInfo += "-" + temp;
194     }
195     const char *region = resLocale->GetRegion();
196     if (region != nullptr && strlen(region) > 0) {
197         temp.assign(region);
198         localeInfo += "-" + temp;
199     }
200     return localeInfo;
201 }
202 
LocalizeNumber(std::string & inputOutputNum,const ResConfigImpl & resConfig,const int32_t precision=INVALID_PRECISION)203 bool LocalizeNumber(std::string &inputOutputNum, const ResConfigImpl &resConfig,
204     const int32_t precision = INVALID_PRECISION)
205 {
206 #ifdef SUPPORT_GRAPHICS
207     const ResLocale *resLocale = resConfig.GetResLocale();
208     if (resLocale == nullptr) {
209         RESMGR_HILOGW(RESMGR_TAG, "LocalizeNumber resLocale is null");
210         return true;
211     }
212 
213     std::string localeInfo = GetLocalInfo(resLocale);
214     if (localeInfo.empty()) {
215         return true;
216     }
217     icu::Locale locale(localeInfo.c_str());
218     if (locale.isBogus()) {
219         return true;
220     }
221 
222     icu::number::LocalizedNumberFormatter numberFormat = icu::number::NumberFormatter::withLocale(locale);
223     numberFormat = numberFormat.grouping(UNumberGroupingStrategy::UNUM_GROUPING_OFF);
224     numberFormat = numberFormat.roundingMode(UNUM_ROUND_HALFUP);
225     if (precision != INVALID_PRECISION) {
226         numberFormat = numberFormat.precision(icu::number::Precision::fixedFraction(precision));
227     }
228     double num;
229     if (!Utils::convertToDouble(inputOutputNum, num)) {
230         return false;
231     }
232     inputOutputNum.clear();
233     UErrorCode status = U_ZERO_ERROR;
234     numberFormat.formatDouble(num, status).toString(status).toUTF8String(inputOutputNum);
235     if (status == U_ZERO_ERROR) {
236         RESMGR_HILOGE(RESMGR_TAG, "LocalizeNumber failed, status = %{public}d", status);
237         return false;
238     }
239     return true;
240 #else
241     return true;
242 #endif
243 }
244 
GetReplaceStr(const std::tuple<ResourceManager::NapiValueType,std::string> & jsParam,const std::string & placeHolderType,int32_t precision,const ResConfigImpl & config,std::string & replaceStr)245 bool GetReplaceStr(const std::tuple<ResourceManager::NapiValueType, std::string> &jsParam,
246     const std::string &placeHolderType, int32_t precision, const ResConfigImpl &config, std::string &replaceStr)
247 {
248     ResourceManager::NapiValueType paramType = std::get<0>(jsParam);
249     std::string paramValue = std::get<1>(jsParam);
250 
251     // string type
252     if (placeHolderType == "s") {
253         if (paramType != ResourceManager::NapiValueType::NAPI_STRING) {
254             RESMGR_HILOGE(RESMGR_TAG, "the type of placeholder and param does not match");
255             return false;
256         }
257         replaceStr = paramValue;
258         return true;
259     }
260 
261     // number type
262     if (paramType != ResourceManager::NapiValueType::NAPI_NUMBER) {
263         RESMGR_HILOGE(RESMGR_TAG, "the type of placeholder and param does not match");
264         return false;
265     }
266 
267     // int type
268     if (placeHolderType == "d") {
269         size_t posOfDecimalPoint = paramValue.find(".");
270         replaceStr = paramValue.substr(0, posOfDecimalPoint);
271         return LocalizeNumber(replaceStr, config);
272     }
273 
274     // double type
275     replaceStr = paramValue;
276     return LocalizeNumber(replaceStr, config, precision);
277 }
278 
MatchPlaceholderIndex(std::string placeholderIndex,size_t & paramIndex,size_t & matchCount)279 bool MatchPlaceholderIndex(std::string placeholderIndex, size_t &paramIndex, size_t &matchCount)
280 {
281     if (placeholderIndex.length() != 0) {
282         if (placeholderIndex.size() > SIZE_T_MAX_STR.size() ||
283             (placeholderIndex.size() == SIZE_T_MAX_STR.size() && placeholderIndex > SIZE_T_MAX_STR)) {
284             RESMGR_HILOGE(RESMGR_TAG, "index of placeholder is too large");
285             return false;
286         }
287         unsigned long index;
288         if (!Utils::convertToUnsignedLong(placeholderIndex, index) || index < 1) {
289             return false;
290         }
291         paramIndex = index - 1;
292     } else {
293         paramIndex = matchCount++;
294     }
295     return true;
296 }
297 
GetPrecision(const std::string & precisionStr)298 int32_t GetPrecision(const std::string &precisionStr)
299 {
300     size_t posOfDecimalPoint = precisionStr.find(".");
301     if (posOfDecimalPoint != std::string::npos) {
302         return std::stoi(precisionStr.substr(posOfDecimalPoint + 1));
303     }
304     return INVALID_PRECISION;
305 }
306 
ReplacePlaceholderWithParams(std::string & inputOutputValue,const ResConfigImpl & resConfig,const std::vector<std::tuple<ResourceManager::NapiValueType,std::string>> & jsParams)307 bool ReplacePlaceholderWithParams(std::string &inputOutputValue, const ResConfigImpl &resConfig,
308     const std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
309 {
310     if (inputOutputValue.empty()) {
311         return true;
312     }
313 
314     std::string::const_iterator start = inputOutputValue.begin();
315     std::string::const_iterator end = inputOutputValue.end();
316     std::smatch matches;
317     size_t matchCount = 0;
318     int prefixLength = 0;
319 
320     while (std::regex_search(start, end, matches, PLACEHOLDER_MATCHING_RULES)) {
321         prefixLength = matches[MATCHE_INDEX_WHOLE_STRING].first - inputOutputValue.begin();
322         // Matched to %%, replace it with %
323         if (matches[MATCHE_INDEX_DOUBLE_PERCENT].length() != 0) {
324             inputOutputValue.erase(matches[MATCHE_INDEX_DOUBLE_PERCENT].first);
325             start = inputOutputValue.begin() + prefixLength + 1;
326             end = inputOutputValue.end();
327             continue;
328         } else if (jsParams.size() == 0) { // Matched to placeholder but no params, ignore placehold
329             start = inputOutputValue.begin() + prefixLength + matches[0].length();
330             continue;
331         }
332 
333         // Matched to placeholder, check and parse param index
334         std::string placeholderIndex = matches[MATCHE_INDEX_PLACEHOLDER_INDEX];
335         size_t paramIndex;
336         if (!MatchPlaceholderIndex(placeholderIndex, paramIndex, matchCount)) {
337             return false;
338         }
339         if (paramIndex >= jsParams.size()) {
340             RESMGR_HILOGE(RESMGR_TAG, "index of placeholder out of range");
341             return false;
342         }
343         // Replace placeholder with corresponding param
344         std::string replaceStr;
345         int32_t precision = GetPrecision(matches[MATCHE_INDEX_PRECISION]);
346         std::string placeholderType = matches[MATCHE_INDEX_PLACEHOLDER_TYPE];
347         if (!GetReplaceStr(jsParams[paramIndex], placeholderType, precision, resConfig, replaceStr)) {
348             return false;
349         }
350         inputOutputValue.replace(prefixLength, matches[MATCHE_INDEX_WHOLE_STRING].length(), replaceStr);
351 
352         // Update iterator
353         start = inputOutputValue.begin() + prefixLength + replaceStr.length();
354         end = inputOutputValue.end();
355     }
356     return true;
357 }
358 } // namespace Resource
359 } // namespace Global
360 } // namespace OHOS