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 "i18n_memory_adapter.h"
17 #include "securec.h"
18 #include "str_util.h"
19 #include "types.h"
20 #include "locale_info.h"
21 
22 using namespace OHOS::I18N;
23 
Init(const char * newLang,const char * newScript,const char * newRegion,int & status)24 void LocaleInfo::Init(const char *newLang, const char *newScript, const char *newRegion, int &status)
25 {
26     id = nullptr;
27     status = IERROR;
28     if (newLang == nullptr) {
29         return;
30     }
31     int langLength = LenCharArray(newLang);
32     // language consists of two or three letters
33     if ((langLength > LANGUAGE_MAX_LENGTH) || (langLength < LANGUAGE_MIN_LENGTH)) {
34         return;
35     }
36     I18nFree(static_cast<void *>(language));
37     language = NewArrayAndCopy(newLang, langLength);
38     if (newScript != nullptr) {
39         int scriptLength = LenCharArray(newScript);
40         if (scriptLength == SCRIPT_LENGTH) {
41             script = NewArrayAndCopy(newScript, scriptLength);
42         }
43     }
44     if (newRegion != nullptr) {
45         int regionLength = LenCharArray(newRegion);
46         if (regionLength == REGION_LENGTH) {
47             region = NewArrayAndCopy(newRegion, regionLength);
48         }
49     }
50     InitIdstr();
51     status = ISUCCESS;
52 }
53 
InitIdstr()54 void LocaleInfo::InitIdstr()
55 {
56     if (language == nullptr) {
57         return;
58     }
59     std::string idStr(language);
60     // script consists of four letters
61     if ((script != nullptr) && (LenCharArray(script) > 0)) {
62         idStr = idStr + "-" + script;
63     }
64     if ((region != nullptr) && (LenCharArray(region) > 0)) {
65         idStr = idStr + "-" + region;
66     }
67     I18nFree(static_cast<void *>(id));
68     id = NewArrayAndCopy(idStr.data(), idStr.size());
69 }
70 
LocaleInfo(const char * newLang,const char * newScript,const char * newRegion)71 LocaleInfo::LocaleInfo(const char *newLang, const char *newScript, const char *newRegion)
72 {
73     int status = ISUCCESS;
74     Init(newLang, newScript, newRegion, status);
75     if (status != ISUCCESS) {
76         SetFail();
77     }
78 }
79 
IsDefaultLocale() const80 bool LocaleInfo::IsDefaultLocale() const
81 {
82     if ((GetLanguage() == nullptr) || (GetRegion() == nullptr)) {
83         return false;
84     }
85     return ((strcmp(GetLanguage(), "en") == 0) && (strcmp(GetRegion(), "US") == 0));
86 }
87 
LocaleInfo(const char * newLang,const char * newRegion)88 LocaleInfo::LocaleInfo(const char *newLang, const char *newRegion)
89 {
90     int status = ISUCCESS;
91     Init(newLang, nullptr, newRegion, status);
92     if (status != ISUCCESS) {
93         SetFail();
94     }
95 }
96 
LocaleInfo()97 LocaleInfo::LocaleInfo()
98 {
99     id = nullptr;
100     SetFail();
101 }
102 
LocaleInfo(const LocaleInfo & o)103 LocaleInfo::LocaleInfo(const LocaleInfo &o)
104 {
105     int status = ISUCCESS;
106     Init(o.language, o.script, o.region, status);
107     if (status != ISUCCESS) {
108         SetFail();
109     }
110 }
111 
~LocaleInfo()112 LocaleInfo::~LocaleInfo()
113 {
114     FreeResource();
115 }
116 
FreeResource()117 void LocaleInfo::FreeResource()
118 {
119     I18nFree(static_cast<void *>(language));
120     I18nFree(static_cast<void *>(script));
121     I18nFree(static_cast<void *>(region));
122     I18nFree(static_cast<void *>(id));
123     I18nFree(static_cast<void *>(numberDigits));
124 }
125 
operator ==(const LocaleInfo & other) const126 bool LocaleInfo::operator ==(const LocaleInfo &other) const
127 {
128     bool ret = CompareLocaleItem(language, other.language);
129     if (!ret) {
130         return false;
131     }
132     ret = CompareLocaleItem(script, other.script);
133     if (!ret) {
134         return false;
135     }
136     ret = CompareLocaleItem(region, other.region);
137     return ret;
138 }
139 
operator =(const LocaleInfo & o)140 LocaleInfo &LocaleInfo::operator =(const LocaleInfo &o)
141 {
142     if (&o == this) {
143         return *this;
144     }
145     FreeResource();
146     if (o.language != nullptr) {
147         language = NewArrayAndCopy(o.language, strlen(o.language));
148     }
149     if (o.script != nullptr) {
150         script = NewArrayAndCopy(o.script, strlen(o.script));
151     }
152     if (o.region != nullptr) {
153         region = NewArrayAndCopy(o.region, strlen(o.region));
154     }
155     if (o.id != nullptr) {
156         id = NewArrayAndCopy(o.id, LenCharArray(o.id));
157     }
158     if (o.numberDigits != nullptr) {
159         numberDigits = NewArrayAndCopy(o.numberDigits, LenCharArray(o.numberDigits));
160     }
161     return *this;
162 }
163 
GetLanguage() const164 const char *LocaleInfo::GetLanguage() const
165 {
166     return language;
167 }
168 
GetScript() const169 const char *LocaleInfo::GetScript() const
170 {
171     return script;
172 }
173 
GetRegion() const174 const char *LocaleInfo::GetRegion() const
175 {
176     return region;
177 }
178 
GetId() const179 const char *LocaleInfo::GetId() const
180 {
181     const char *rid = id;
182     return rid;
183 }
184 
IsSuccess()185 bool LocaleInfo::IsSuccess()
186 {
187     bool r = isSucc;
188     isSucc = true;
189     return r;
190 }
191 
SetFail()192 void LocaleInfo::SetFail()
193 {
194     isSucc = false;
195 }
196 
ChangeLanguageCode(char * lang,const int32_t dstSize,const char * src,const int32_t srcSize) const197 bool LocaleInfo::ChangeLanguageCode(char *lang, const int32_t dstSize, const char *src, const int32_t srcSize) const
198 {
199     if (lang == nullptr || src == nullptr) {
200         return false;
201     }
202     if (srcSize == (LANGUAGE_MIN_LENGTH + 1)) { // three letter language only support fil and mai
203         if (memcmp(src, "fil", srcSize) == 0) {
204             lang[0] = 't';
205             lang[1] = 'l';
206         } else if (memcmp(src, "mai", srcSize) == 0) {
207             lang[0] = 'm';
208             lang[1] = 'd';
209         } else {
210             return false;
211         }
212         return true;
213     } else if (srcSize == LANGUAGE_MIN_LENGTH) {
214         if (memcmp(src, "he", srcSize) == 0) {
215             lang[0] = 'i';
216             lang[1] = 'w';
217         } else if (memcmp(src, "id", srcSize) == 0) {
218             lang[0] = 'i';
219             lang[1] = 'n';
220         } else {
221             if (strcpy_s(lang, dstSize, src) != EOK) {
222                 return false;
223             }
224         }
225         return true;
226     }
227     return false;
228 }
229 
GetMask() const230 uint32_t LocaleInfo::GetMask() const
231 {
232     if (language == nullptr) {
233         return 0;
234     }
235     char lang[LANGUAGE_MAX_LENGTH];
236     bool isRight = ChangeLanguageCode(lang, LANGUAGE_MAX_LENGTH, language, LenCharArray(language));
237     if (!isRight) {
238         return 0;
239     }
240     // use 7bit to represent an English letter,
241     // 32--- language ---18--- script ---14--- region ---0
242     uint32_t tempLangFirst = (lang[0] - CHAR_OFF);
243     uint32_t tempLangSecond = (lang[1] - CHAR_OFF);
244     uint32_t mask = (tempLangFirst << LANG_FIRST_BEGIN) | (tempLangSecond << LANG_SECOND_BEGIN);
245     if ((script != nullptr) && (LenCharArray(script) > 0)) {
246         if (strcmp(script, "Hans") == 0) {
247             mask = mask | (HANS << SCRIPT_BEGIN);
248         } else if (strcmp(script, "Hant") == 0) {
249             mask = mask | (HANT << SCRIPT_BEGIN);
250         } else if (strcmp(script, "Latn") == 0) {
251             mask = mask | (LATN << SCRIPT_BEGIN);
252         } else if (strcmp(script, "Qaag") == 0) {
253             mask = mask | (QAAG << SCRIPT_BEGIN);
254         } else if (strcmp(script, "Cyrl") == 0) {
255             mask = mask | (CYRL << SCRIPT_BEGIN);
256         } else if (strcmp(script, "Deva") == 0) {
257             mask = mask | (DEVA << SCRIPT_BEGIN);
258         } else if (strcmp(script, "Guru") == 0) {
259             mask = mask | (GURU << SCRIPT_BEGIN);
260         }
261     }
262     if ((region != nullptr) && (LenCharArray(region) > 1)) {
263         uint32_t tempRegion = (region[0] - CHAR_OFF);
264         uint32_t tempRegionSecond = (region[1] - CHAR_OFF);
265         mask = mask | (tempRegion << REGION_FIRST_LETTER) | (tempRegionSecond);
266     }
267     return mask;
268 }
269 
ForLanguageTag(const char * languageTag,I18nStatus & status)270 LocaleInfo LocaleInfo::ForLanguageTag(const char *languageTag, I18nStatus &status)
271 {
272     LocaleInfo locale;
273     if (languageTag == nullptr) {
274         status = IERROR;
275         return locale;
276     }
277     ParseLanguageTag(locale, languageTag, status);
278     locale.InitIdstr();
279     return locale;
280 }
281 
ParseLanguageTag(LocaleInfo & locale,const char * languageTag,I18nStatus & status)282 void LocaleInfo::ParseLanguageTag(LocaleInfo &locale, const char *languageTag, I18nStatus &status)
283 {
284     const char *tag = languageTag;
285     uint16_t options = OPT_LANG;
286     const char *key = nullptr;
287     const char *value = nullptr;
288     uint8_t type = 0;
289     while (tag) {
290         const char *start = tag;
291         const char *end = tag;
292         while (*end) {
293             if (*end == '-') {
294                 break;
295             }
296             ++end;
297         }
298         tag = end + 1;
299         if (*end == '\0') {
300             tag = nullptr;
301         }
302         auto tagLength = end - start;
303         ConfirmTagType(start, tagLength, type, key, value);
304         if (!ParseNormalSubTag(locale, start, tagLength, options, type)) {
305             if ((options & OPT_EXTENSION) && (type == TAG_VALUE)) {
306                 ProcessExtension(locale, key, value);
307                 type = TAG_COMMON;
308             }
309         }
310     }
311     I18nFree(static_cast<void *>(const_cast<char *>(key)));
312     I18nFree(static_cast<void *>(const_cast<char *>(value)));
313 }
314 
ParseNormalSubTag(LocaleInfo & locale,const char * start,size_t tagLength,uint16_t & options,uint8_t & type)315 bool LocaleInfo::ParseNormalSubTag(LocaleInfo &locale, const char *start, size_t tagLength, uint16_t &options,
316     uint8_t &type)
317 {
318     if ((start == nullptr) || (tagLength == 0)) {
319         return false;
320     }
321     if ((options & OPT_LANG) && (type == TAG_COMMON)) {
322         if (IsLanguage(start, tagLength)) {
323             locale.language = I18nNewCharString(start, tagLength);
324             options &= ~OPT_LANG;
325             options |= OPT_SCRIPT | OPT_REGION | OPT_EXTENSION;
326             return true;
327         }
328     }
329     if ((options & OPT_SCRIPT) && (type == TAG_COMMON)) {
330         if (IsScript(start, tagLength)) {
331             options &= ~OPT_SCRIPT;
332             locale.script = I18nNewCharString(start, tagLength);
333             return true;
334         }
335     }
336     if ((options & OPT_REGION) && (type == TAG_COMMON)) {
337         if (IsRegion(start, tagLength)) {
338             options &= ~OPT_REGION;
339             options &= ~OPT_SCRIPT;
340             locale.region = I18nNewCharString(start, tagLength);
341             return true;
342         }
343     }
344     return false;
345 }
346 
ConfirmTagType(const char * start,size_t length,uint8_t & type,const char * & key,const char * & value)347 void LocaleInfo::ConfirmTagType(const char *start, size_t length, uint8_t &type, const char* &key, const char* &value)
348 {
349     if (start == nullptr) {
350         return;
351     }
352     switch (type) {
353         case TAG_COMMON: {
354             if ((length == 1) && (*start == 'u')) {
355                 type = TAG_U;
356             }
357             return;
358         }
359         case TAG_U: {
360             type = TAG_KEY;
361             I18nFree(static_cast<void *>(const_cast<char *>(key)));
362             key = I18nNewCharString(start, length);
363             return;
364         }
365         case TAG_KEY: {
366             type = TAG_VALUE;
367             I18nFree(static_cast<void *>(const_cast<char *>(value)));
368             value = I18nNewCharString(start, length);
369             return;
370         }
371         default: {
372             type = TAG_COMMON;
373             return;
374         }
375     }
376 }
377 
ProcessExtension(LocaleInfo & locale,const char * key,const char * value)378 void LocaleInfo::ProcessExtension(LocaleInfo &locale, const char *key, const char *value)
379 {
380     if (key == nullptr || value == nullptr) {
381         return;
382     }
383     // now we only support numbering systems in extensions
384     if (strcmp(key, "nu") == 0) {
385         locale.numberDigits = NewArrayAndCopy(value, strlen(value));
386         return;
387     }
388 }
389 
IsLanguage(const char * start,uint8_t length)390 bool LocaleInfo::IsLanguage(const char *start, uint8_t length)
391 {
392     if ((length != LANGUAGE_MAX_LENGTH) && (length != LANGUAGE_MIN_LENGTH)) {
393         return false;
394     }
395     for (uint8_t i = 0; i < length; ++i) {
396         const char ch = *(start + i);
397         if (ch < 'a' || ch > 'z') {
398             return false;
399         }
400     }
401     return true;
402 }
403 
IsScript(const char * start,uint8_t length)404 bool LocaleInfo::IsScript(const char *start, uint8_t length)
405 {
406     // all scripts's length is 4,
407     // now we support Latn, Hans, Hant, Qaag, Cyrl, Deva, Guru
408     if (length != SCRIPT_LENGTH || start == nullptr) {
409         return false;
410     }
411     if (memcmp(start, "Hans", length) == 0) {
412         return true;
413     } else if (memcmp(start, "Latn", length) == 0) {
414         return true;
415     } else if (memcmp(start, "Hant", length) == 0) {
416         return true;
417     } else if (memcmp(start, "Qaag", length) == 0) {
418         return true;
419     } else if (memcmp(start, "Cyrl", length) == 0) {
420         return true;
421     } else if (memcmp(start, "Deva", length) == 0) {
422         return true;
423     } else if (memcmp(start, "Guru", length) == 0) {
424         return true;
425     } else {
426         return false;
427     }
428 }
429 
IsRegion(const char * start,uint8_t length)430 bool LocaleInfo::IsRegion(const char *start, uint8_t length)
431 {
432     if (length != REGION_LENGTH) {
433         return false;
434     }
435     for (uint8_t i = 0; i < length; ++i) {
436         const char ch = *(start + i);
437         if (ch < 'A' || ch > 'Z') { // region characters should all be upper case.
438             return false;
439         }
440     }
441     return true;
442 }
443 
GetExtension(const char * key)444 const char *LocaleInfo::GetExtension(const char *key)
445 {
446     if (strcmp(key, "nu") == 0) {
447         return numberDigits;
448     }
449     return nullptr;
450 }
451