1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 * Copyright (C) 1997-2016, International Business Machines
7 * Corporation and others. All Rights Reserved.
8 *
9 *******************************************************************************
10 * file name: locdispnames.cpp
11 * encoding: UTF-8
12 * tab size: 8 (not used)
13 * indentation:4
14 *
15 * created on: 2010feb25
16 * created by: Markus W. Scherer
17 *
18 * Code for locale display names, separated out from other .cpp files
19 * that then do not depend on resource bundle code and display name data.
20 */
21
22 #include "unicode/utypes.h"
23 #include "unicode/brkiter.h"
24 #include "unicode/locid.h"
25 #include "unicode/uenum.h"
26 #include "unicode/uloc.h"
27 #include "unicode/ures.h"
28 #include "unicode/ustring.h"
29 #include "charstr.h"
30 #include "cmemory.h"
31 #include "cstring.h"
32 #include "putilimp.h"
33 #include "ulocimp.h"
34 #include "uresimp.h"
35 #include "ureslocs.h"
36 #include "ustr_imp.h"
37
38 // C++ API ----------------------------------------------------------------- ***
39
40 U_NAMESPACE_BEGIN
41
42 UnicodeString&
getDisplayLanguage(UnicodeString & dispLang) const43 Locale::getDisplayLanguage(UnicodeString& dispLang) const
44 {
45 return this->getDisplayLanguage(getDefault(), dispLang);
46 }
47
48 /*We cannot make any assumptions on the size of the output display strings
49 * Yet, since we are calling through to a C API, we need to set limits on
50 * buffer size. For all the following getDisplay functions we first attempt
51 * to fill up a stack allocated buffer. If it is to small we heap allocated
52 * the exact buffer we need copy it to the UnicodeString and delete it*/
53
54 UnicodeString&
getDisplayLanguage(const Locale & displayLocale,UnicodeString & result) const55 Locale::getDisplayLanguage(const Locale &displayLocale,
56 UnicodeString &result) const {
57 char16_t *buffer;
58 UErrorCode errorCode=U_ZERO_ERROR;
59 int32_t length;
60
61 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY);
62 if (buffer == nullptr) {
63 result.truncate(0);
64 return result;
65 }
66
67 length=uloc_getDisplayLanguage(fullName, displayLocale.fullName,
68 buffer, result.getCapacity(),
69 &errorCode);
70 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
71
72 if(errorCode==U_BUFFER_OVERFLOW_ERROR) {
73 buffer=result.getBuffer(length);
74 if (buffer == nullptr) {
75 result.truncate(0);
76 return result;
77 }
78 errorCode=U_ZERO_ERROR;
79 length=uloc_getDisplayLanguage(fullName, displayLocale.fullName,
80 buffer, result.getCapacity(),
81 &errorCode);
82 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
83 }
84
85 return result;
86 }
87
88 UnicodeString&
getDisplayScript(UnicodeString & dispScript) const89 Locale::getDisplayScript(UnicodeString& dispScript) const
90 {
91 return this->getDisplayScript(getDefault(), dispScript);
92 }
93
94 UnicodeString&
getDisplayScript(const Locale & displayLocale,UnicodeString & result) const95 Locale::getDisplayScript(const Locale &displayLocale,
96 UnicodeString &result) const {
97 char16_t *buffer;
98 UErrorCode errorCode=U_ZERO_ERROR;
99 int32_t length;
100
101 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY);
102 if (buffer == nullptr) {
103 result.truncate(0);
104 return result;
105 }
106
107 length=uloc_getDisplayScript(fullName, displayLocale.fullName,
108 buffer, result.getCapacity(),
109 &errorCode);
110 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
111
112 if(errorCode==U_BUFFER_OVERFLOW_ERROR) {
113 buffer=result.getBuffer(length);
114 if (buffer == nullptr) {
115 result.truncate(0);
116 return result;
117 }
118 errorCode=U_ZERO_ERROR;
119 length=uloc_getDisplayScript(fullName, displayLocale.fullName,
120 buffer, result.getCapacity(),
121 &errorCode);
122 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
123 }
124
125 return result;
126 }
127
128 UnicodeString&
getDisplayCountry(UnicodeString & dispCntry) const129 Locale::getDisplayCountry(UnicodeString& dispCntry) const
130 {
131 return this->getDisplayCountry(getDefault(), dispCntry);
132 }
133
134 UnicodeString&
getDisplayCountry(const Locale & displayLocale,UnicodeString & result) const135 Locale::getDisplayCountry(const Locale &displayLocale,
136 UnicodeString &result) const {
137 char16_t *buffer;
138 UErrorCode errorCode=U_ZERO_ERROR;
139 int32_t length;
140
141 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY);
142 if (buffer == nullptr) {
143 result.truncate(0);
144 return result;
145 }
146
147 length=uloc_getDisplayCountry(fullName, displayLocale.fullName,
148 buffer, result.getCapacity(),
149 &errorCode);
150 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
151
152 if(errorCode==U_BUFFER_OVERFLOW_ERROR) {
153 buffer=result.getBuffer(length);
154 if (buffer == nullptr) {
155 result.truncate(0);
156 return result;
157 }
158 errorCode=U_ZERO_ERROR;
159 length=uloc_getDisplayCountry(fullName, displayLocale.fullName,
160 buffer, result.getCapacity(),
161 &errorCode);
162 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
163 }
164
165 return result;
166 }
167
168 UnicodeString&
getDisplayVariant(UnicodeString & dispVar) const169 Locale::getDisplayVariant(UnicodeString& dispVar) const
170 {
171 return this->getDisplayVariant(getDefault(), dispVar);
172 }
173
174 UnicodeString&
getDisplayVariant(const Locale & displayLocale,UnicodeString & result) const175 Locale::getDisplayVariant(const Locale &displayLocale,
176 UnicodeString &result) const {
177 char16_t *buffer;
178 UErrorCode errorCode=U_ZERO_ERROR;
179 int32_t length;
180
181 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY);
182 if (buffer == nullptr) {
183 result.truncate(0);
184 return result;
185 }
186
187 length=uloc_getDisplayVariant(fullName, displayLocale.fullName,
188 buffer, result.getCapacity(),
189 &errorCode);
190 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
191
192 if(errorCode==U_BUFFER_OVERFLOW_ERROR) {
193 buffer=result.getBuffer(length);
194 if (buffer == nullptr) {
195 result.truncate(0);
196 return result;
197 }
198 errorCode=U_ZERO_ERROR;
199 length=uloc_getDisplayVariant(fullName, displayLocale.fullName,
200 buffer, result.getCapacity(),
201 &errorCode);
202 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
203 }
204
205 return result;
206 }
207
208 UnicodeString&
getDisplayName(UnicodeString & name) const209 Locale::getDisplayName( UnicodeString& name ) const
210 {
211 return this->getDisplayName(getDefault(), name);
212 }
213
214 UnicodeString&
getDisplayName(const Locale & displayLocale,UnicodeString & result) const215 Locale::getDisplayName(const Locale &displayLocale,
216 UnicodeString &result) const {
217 char16_t *buffer;
218 UErrorCode errorCode=U_ZERO_ERROR;
219 int32_t length;
220
221 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY);
222 if (buffer == nullptr) {
223 result.truncate(0);
224 return result;
225 }
226
227 length=uloc_getDisplayName(fullName, displayLocale.fullName,
228 buffer, result.getCapacity(),
229 &errorCode);
230 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
231
232 if(errorCode==U_BUFFER_OVERFLOW_ERROR) {
233 buffer=result.getBuffer(length);
234 if (buffer == nullptr) {
235 result.truncate(0);
236 return result;
237 }
238 errorCode=U_ZERO_ERROR;
239 length=uloc_getDisplayName(fullName, displayLocale.fullName,
240 buffer, result.getCapacity(),
241 &errorCode);
242 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0);
243 }
244
245 return result;
246 }
247
248 #if ! UCONFIG_NO_BREAK_ITERATION
249
250 // -------------------------------------
251 // Gets the objectLocale display name in the default locale language.
252 UnicodeString& U_EXPORT2
getDisplayName(const Locale & objectLocale,UnicodeString & name)253 BreakIterator::getDisplayName(const Locale& objectLocale,
254 UnicodeString& name)
255 {
256 return objectLocale.getDisplayName(name);
257 }
258
259 // -------------------------------------
260 // Gets the objectLocale display name in the displayLocale language.
261 UnicodeString& U_EXPORT2
getDisplayName(const Locale & objectLocale,const Locale & displayLocale,UnicodeString & name)262 BreakIterator::getDisplayName(const Locale& objectLocale,
263 const Locale& displayLocale,
264 UnicodeString& name)
265 {
266 return objectLocale.getDisplayName(displayLocale, name);
267 }
268
269 #endif
270
271
272 U_NAMESPACE_END
273
274 // C API ------------------------------------------------------------------- ***
275
276 U_NAMESPACE_USE
277
278 namespace {
279
280 /* ### Constants **************************************************/
281
282 /* These strings describe the resources we attempt to load from
283 the locale ResourceBundle data file.*/
284 constexpr char _kLanguages[] = "Languages";
285 constexpr char _kScripts[] = "Scripts";
286 constexpr char _kScriptsStandAlone[] = "Scripts%stand-alone";
287 constexpr char _kCountries[] = "Countries";
288 constexpr char _kVariants[] = "Variants";
289 constexpr char _kKeys[] = "Keys";
290 constexpr char _kTypes[] = "Types";
291 //constexpr char _kRootName[] = "root";
292 constexpr char _kCurrency[] = "currency";
293 constexpr char _kCurrencies[] = "Currencies";
294 constexpr char _kLocaleDisplayPattern[] = "localeDisplayPattern";
295 constexpr char _kPattern[] = "pattern";
296 constexpr char _kSeparator[] = "separator";
297
298 /* ### Display name **************************************************/
299
300 int32_t
_getStringOrCopyKey(const char * path,const char * locale,const char * tableKey,const char * subTableKey,const char * itemKey,const char * substitute,char16_t * dest,int32_t destCapacity,UErrorCode & errorCode)301 _getStringOrCopyKey(const char *path, const char *locale,
302 const char *tableKey,
303 const char* subTableKey,
304 const char *itemKey,
305 const char *substitute,
306 char16_t *dest, int32_t destCapacity,
307 UErrorCode &errorCode) {
308 if (U_FAILURE(errorCode)) { return 0; }
309 const char16_t *s = nullptr;
310 int32_t length = 0;
311
312 if(itemKey==nullptr) {
313 /* top-level item: normal resource bundle access */
314 icu::LocalUResourceBundlePointer rb(ures_open(path, locale, &errorCode));
315
316 if(U_SUCCESS(errorCode)) {
317 s=ures_getStringByKey(rb.getAlias(), tableKey, &length, &errorCode);
318 /* see comment about closing rb near "return item;" in _res_getTableStringWithFallback() */
319 }
320 } else {
321 bool isLanguageCode = (uprv_strncmp(tableKey, _kLanguages, 9) == 0);
322 /* Language code should not be a number. If it is, set the error code. */
323 if (isLanguageCode && uprv_strtol(itemKey, nullptr, 10)) {
324 errorCode = U_MISSING_RESOURCE_ERROR;
325 } else {
326 /* second-level item, use special fallback */
327 s=uloc_getTableStringWithFallback(path, locale,
328 tableKey,
329 subTableKey,
330 itemKey,
331 &length,
332 &errorCode);
333 if (U_FAILURE(errorCode) && isLanguageCode && itemKey != nullptr) {
334 // convert itemKey locale code to canonical form and try again, ICU-20870
335 errorCode = U_ZERO_ERROR;
336 Locale canonKey = Locale::createCanonical(itemKey);
337 s=uloc_getTableStringWithFallback(path, locale,
338 tableKey,
339 subTableKey,
340 canonKey.getName(),
341 &length,
342 &errorCode);
343 }
344 }
345 }
346
347 if(U_SUCCESS(errorCode)) {
348 int32_t copyLength=uprv_min(length, destCapacity);
349 if(copyLength>0 && s != nullptr) {
350 u_memcpy(dest, s, copyLength);
351 }
352 } else {
353 /* no string from a resource bundle: convert the substitute */
354 length=(int32_t)uprv_strlen(substitute);
355 u_charsToUChars(substitute, dest, uprv_min(length, destCapacity));
356 errorCode = U_USING_DEFAULT_WARNING;
357 }
358
359 return u_terminateUChars(dest, destCapacity, length, &errorCode);
360 }
361
362 using UDisplayNameGetter = icu::CharString(const char*, UErrorCode&);
363
364 int32_t
_getDisplayNameForComponent(const char * locale,const char * displayLocale,char16_t * dest,int32_t destCapacity,UDisplayNameGetter * getter,const char * tag,UErrorCode & errorCode)365 _getDisplayNameForComponent(const char *locale,
366 const char *displayLocale,
367 char16_t *dest, int32_t destCapacity,
368 UDisplayNameGetter *getter,
369 const char *tag,
370 UErrorCode &errorCode) {
371 if (U_FAILURE(errorCode)) { return 0; }
372 UErrorCode localStatus;
373 const char* root = nullptr;
374
375 if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) {
376 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
377 return 0;
378 }
379
380 localStatus = U_ZERO_ERROR;
381 icu::CharString localeBuffer = (*getter)(locale, localStatus);
382 if (U_FAILURE(localStatus)) {
383 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
384 return 0;
385 }
386 if (localeBuffer.isEmpty()) {
387 // For the display name, we treat this as unknown language (ICU-20273).
388 if (getter == ulocimp_getLanguage) {
389 localeBuffer.append("und", errorCode);
390 } else {
391 return u_terminateUChars(dest, destCapacity, 0, &errorCode);
392 }
393 }
394
395 root = tag == _kCountries ? U_ICUDATA_REGION : U_ICUDATA_LANG;
396
397 return _getStringOrCopyKey(root, displayLocale,
398 tag, nullptr, localeBuffer.data(),
399 localeBuffer.data(),
400 dest, destCapacity,
401 errorCode);
402 }
403
404 } // namespace
405
406 U_CAPI int32_t U_EXPORT2
uloc_getDisplayLanguage(const char * locale,const char * displayLocale,char16_t * dest,int32_t destCapacity,UErrorCode * pErrorCode)407 uloc_getDisplayLanguage(const char *locale,
408 const char *displayLocale,
409 char16_t *dest, int32_t destCapacity,
410 UErrorCode *pErrorCode) {
411 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
412 ulocimp_getLanguage, _kLanguages, *pErrorCode);
413 }
414
415 U_CAPI int32_t U_EXPORT2
uloc_getDisplayScript(const char * locale,const char * displayLocale,char16_t * dest,int32_t destCapacity,UErrorCode * pErrorCode)416 uloc_getDisplayScript(const char* locale,
417 const char* displayLocale,
418 char16_t *dest, int32_t destCapacity,
419 UErrorCode *pErrorCode)
420 {
421 if (U_FAILURE(*pErrorCode)) { return 0; }
422 UErrorCode err = U_ZERO_ERROR;
423 int32_t res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
424 ulocimp_getScript, _kScriptsStandAlone, err);
425
426 if (destCapacity == 0 && err == U_BUFFER_OVERFLOW_ERROR) {
427 // For preflight, return the max of the value and the fallback.
428 int32_t fallback_res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
429 ulocimp_getScript, _kScripts, *pErrorCode);
430 return (fallback_res > res) ? fallback_res : res;
431 }
432 if ( err == U_USING_DEFAULT_WARNING ) {
433 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
434 ulocimp_getScript, _kScripts, *pErrorCode);
435 } else {
436 *pErrorCode = err;
437 return res;
438 }
439 }
440
441 static int32_t
uloc_getDisplayScriptInContext(const char * locale,const char * displayLocale,char16_t * dest,int32_t destCapacity,UErrorCode * pErrorCode)442 uloc_getDisplayScriptInContext(const char* locale,
443 const char* displayLocale,
444 char16_t *dest, int32_t destCapacity,
445 UErrorCode *pErrorCode)
446 {
447 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
448 ulocimp_getScript, _kScripts, *pErrorCode);
449 }
450
451 U_CAPI int32_t U_EXPORT2
uloc_getDisplayCountry(const char * locale,const char * displayLocale,char16_t * dest,int32_t destCapacity,UErrorCode * pErrorCode)452 uloc_getDisplayCountry(const char *locale,
453 const char *displayLocale,
454 char16_t *dest, int32_t destCapacity,
455 UErrorCode *pErrorCode) {
456 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
457 ulocimp_getRegion, _kCountries, *pErrorCode);
458 }
459
460 /*
461 * TODO separate variant1_variant2_variant3...
462 * by getting each tag's display string and concatenating them with ", "
463 * in between - similar to uloc_getDisplayName()
464 */
465 U_CAPI int32_t U_EXPORT2
uloc_getDisplayVariant(const char * locale,const char * displayLocale,char16_t * dest,int32_t destCapacity,UErrorCode * pErrorCode)466 uloc_getDisplayVariant(const char *locale,
467 const char *displayLocale,
468 char16_t *dest, int32_t destCapacity,
469 UErrorCode *pErrorCode) {
470 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
471 ulocimp_getVariant, _kVariants, *pErrorCode);
472 }
473
474 /* Instead of having a separate pass for 'special' patterns, reintegrate the two
475 * so we don't get bitten by preflight bugs again. We can be reasonably efficient
476 * without two separate code paths, this code isn't that performance-critical.
477 *
478 * This code is general enough to deal with patterns that have a prefix or swap the
479 * language and remainder components, since we gave developers enough rope to do such
480 * things if they futz with the pattern data. But since we don't give them a way to
481 * specify a pattern for arbitrary combinations of components, there's not much use in
482 * that. I don't think our data includes such patterns, the only variable I know if is
483 * whether there is a space before the open paren, or not. Oh, and zh uses different
484 * chars than the standard open/close paren (which ja and ko use, btw).
485 */
486 U_CAPI int32_t U_EXPORT2
uloc_getDisplayName(const char * locale,const char * displayLocale,char16_t * dest,int32_t destCapacity,UErrorCode * pErrorCode)487 uloc_getDisplayName(const char *locale,
488 const char *displayLocale,
489 char16_t *dest, int32_t destCapacity,
490 UErrorCode *pErrorCode)
491 {
492 static const char16_t defaultSeparator[9] = { 0x007b, 0x0030, 0x007d, 0x002c, 0x0020, 0x007b, 0x0031, 0x007d, 0x0000 }; /* "{0}, {1}" */
493 static const char16_t sub0[4] = { 0x007b, 0x0030, 0x007d , 0x0000 } ; /* {0} */
494 static const char16_t sub1[4] = { 0x007b, 0x0031, 0x007d , 0x0000 } ; /* {1} */
495 static const int32_t subLen = 3;
496 static const char16_t defaultPattern[10] = {
497 0x007b, 0x0030, 0x007d, 0x0020, 0x0028, 0x007b, 0x0031, 0x007d, 0x0029, 0x0000
498 }; /* {0} ({1}) */
499 static const int32_t defaultPatLen = 9;
500 static const int32_t defaultSub0Pos = 0;
501 static const int32_t defaultSub1Pos = 5;
502
503 int32_t length; /* of formatted result */
504
505 const char16_t *separator;
506 int32_t sepLen = 0;
507 const char16_t *pattern;
508 int32_t patLen = 0;
509 int32_t sub0Pos, sub1Pos;
510
511 char16_t formatOpenParen = 0x0028; // (
512 char16_t formatReplaceOpenParen = 0x005B; // [
513 char16_t formatCloseParen = 0x0029; // )
514 char16_t formatReplaceCloseParen = 0x005D; // ]
515
516 UBool haveLang = true; /* assume true, set false if we find we don't have
517 a lang component in the locale */
518 UBool haveRest = true; /* assume true, set false if we find we don't have
519 any other component in the locale */
520 UBool retry = false; /* set true if we need to retry, see below */
521
522 int32_t langi = 0; /* index of the language substitution (0 or 1), virtually always 0 */
523
524 if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) {
525 return 0;
526 }
527
528 if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) {
529 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
530 return 0;
531 }
532
533 {
534 UErrorCode status = U_ZERO_ERROR;
535
536 icu::LocalUResourceBundlePointer locbundle(
537 ures_open(U_ICUDATA_LANG, displayLocale, &status));
538 icu::LocalUResourceBundlePointer dspbundle(
539 ures_getByKeyWithFallback(locbundle.getAlias(), _kLocaleDisplayPattern, nullptr, &status));
540
541 separator=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kSeparator, &sepLen, &status);
542 pattern=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kPattern, &patLen, &status);
543 }
544
545 /* If we couldn't find any data, then use the defaults */
546 if(sepLen == 0) {
547 separator = defaultSeparator;
548 }
549 /* #10244: Even though separator is now a pattern, it is awkward to handle it as such
550 * here since we are trying to build the display string in place in the dest buffer,
551 * and to handle it as a pattern would entail having separate storage for the
552 * substrings that need to be combined (the first of which may be the result of
553 * previous such combinations). So for now we continue to treat the portion between
554 * {0} and {1} as a string to be appended when joining substrings, ignoring anything
555 * that is before {0} or after {1} (no existing separator pattern has any such thing).
556 * This is similar to how pattern is handled below.
557 */
558 {
559 char16_t *p0=u_strstr(separator, sub0);
560 char16_t *p1=u_strstr(separator, sub1);
561 if (p0==nullptr || p1==nullptr || p1<p0) {
562 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
563 return 0;
564 }
565 separator = (const char16_t *)p0 + subLen;
566 sepLen = static_cast<int32_t>(p1 - separator);
567 }
568
569 if(patLen==0 || (patLen==defaultPatLen && !u_strncmp(pattern, defaultPattern, patLen))) {
570 pattern=defaultPattern;
571 patLen=defaultPatLen;
572 sub0Pos=defaultSub0Pos;
573 sub1Pos=defaultSub1Pos;
574 // use default formatOpenParen etc. set above
575 } else { /* non-default pattern */
576 char16_t *p0=u_strstr(pattern, sub0);
577 char16_t *p1=u_strstr(pattern, sub1);
578 if (p0==nullptr || p1==nullptr) {
579 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
580 return 0;
581 }
582 sub0Pos = static_cast<int32_t>(p0-pattern);
583 sub1Pos = static_cast<int32_t>(p1-pattern);
584 if (sub1Pos < sub0Pos) { /* a very odd pattern */
585 int32_t t=sub0Pos; sub0Pos=sub1Pos; sub1Pos=t;
586 langi=1;
587 }
588 if (u_strchr(pattern, 0xFF08) != nullptr) {
589 formatOpenParen = 0xFF08; // fullwidth (
590 formatReplaceOpenParen = 0xFF3B; // fullwidth [
591 formatCloseParen = 0xFF09; // fullwidth )
592 formatReplaceCloseParen = 0xFF3D; // fullwidth ]
593 }
594 }
595
596 /* We loop here because there is one case in which after the first pass we could need to
597 * reextract the data. If there's initial padding before the first element, we put in
598 * the padding and then write that element. If it turns out there's no second element,
599 * we didn't need the padding. If we do need the data (no preflight), and the first element
600 * would have fit but for the padding, we need to reextract. In this case (only) we
601 * adjust the parameters so padding is not added, and repeat.
602 */
603 do {
604 char16_t* p=dest;
605 int32_t patPos=0; /* position in the pattern, used for non-substitution portions */
606 int32_t langLen=0; /* length of language substitution */
607 int32_t langPos=0; /* position in output of language substitution */
608 int32_t restLen=0; /* length of 'everything else' substitution */
609 int32_t restPos=0; /* position in output of 'everything else' substitution */
610 icu::LocalUEnumerationPointer kenum; /* keyword enumeration */
611
612 /* prefix of pattern, extremely likely to be empty */
613 if(sub0Pos) {
614 if(destCapacity >= sub0Pos) {
615 while (patPos < sub0Pos) {
616 *p++ = pattern[patPos++];
617 }
618 } else {
619 patPos=sub0Pos;
620 }
621 length=sub0Pos;
622 } else {
623 length=0;
624 }
625
626 for(int32_t subi=0,resti=0;subi<2;) { /* iterate through patterns 0 and 1*/
627 UBool subdone = false; /* set true when ready to move to next substitution */
628
629 /* prep p and cap for calls to get display components, pin cap to 0 since
630 they complain if cap is negative */
631 int32_t cap=destCapacity-length;
632 if (cap <= 0) {
633 cap=0;
634 } else {
635 p=dest+length;
636 }
637
638 if (subi == langi) { /* {0}*/
639 if(haveLang) {
640 langPos=length;
641 langLen=uloc_getDisplayLanguage(locale, displayLocale, p, cap, pErrorCode);
642 length+=langLen;
643 haveLang=langLen>0;
644 }
645 subdone=true;
646 } else { /* {1} */
647 if(!haveRest) {
648 subdone=true;
649 } else {
650 int32_t len; /* length of component (plus other stuff) we just fetched */
651 switch(resti++) {
652 case 0:
653 restPos=length;
654 len=uloc_getDisplayScriptInContext(locale, displayLocale, p, cap, pErrorCode);
655 break;
656 case 1:
657 len=uloc_getDisplayCountry(locale, displayLocale, p, cap, pErrorCode);
658 break;
659 case 2:
660 len=uloc_getDisplayVariant(locale, displayLocale, p, cap, pErrorCode);
661 break;
662 case 3:
663 kenum.adoptInstead(uloc_openKeywords(locale, pErrorCode));
664 U_FALLTHROUGH;
665 default: {
666 const char* kw=uenum_next(kenum.getAlias(), &len, pErrorCode);
667 if (kw == nullptr) {
668 len=0; /* mark that we didn't add a component */
669 subdone=true;
670 } else {
671 /* incorporating this behavior into the loop made it even more complex,
672 so just special case it here */
673 len = uloc_getDisplayKeyword(kw, displayLocale, p, cap, pErrorCode);
674 if(len) {
675 if(len < cap) {
676 p[len]=0x3d; /* '=', assume we'll need it */
677 }
678 len+=1;
679
680 /* adjust for call to get keyword */
681 cap-=len;
682 if(cap <= 0) {
683 cap=0;
684 } else {
685 p+=len;
686 }
687 }
688 /* reset for call below */
689 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) {
690 *pErrorCode=U_ZERO_ERROR;
691 }
692 int32_t vlen = uloc_getDisplayKeywordValue(locale, kw, displayLocale,
693 p, cap, pErrorCode);
694 if(len) {
695 if(vlen==0) {
696 --len; /* remove unneeded '=' */
697 }
698 /* restore cap and p to what they were at start */
699 cap=destCapacity-length;
700 if(cap <= 0) {
701 cap=0;
702 } else {
703 p=dest+length;
704 }
705 }
706 len+=vlen; /* total we added for key + '=' + value */
707 }
708 } break;
709 } /* end switch */
710
711 if (len>0) {
712 /* we added a component, so add separator and write it if there's room. */
713 if(len+sepLen<=cap) {
714 const char16_t * plimit = p + len;
715 for (; p < plimit; p++) {
716 if (*p == formatOpenParen) {
717 *p = formatReplaceOpenParen;
718 } else if (*p == formatCloseParen) {
719 *p = formatReplaceCloseParen;
720 }
721 }
722 for(int32_t i=0;i<sepLen;++i) {
723 *p++=separator[i];
724 }
725 }
726 length+=len+sepLen;
727 } else if(subdone) {
728 /* remove separator if we added it */
729 if (length!=restPos) {
730 length-=sepLen;
731 }
732 restLen=length-restPos;
733 haveRest=restLen>0;
734 }
735 }
736 }
737
738 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) {
739 *pErrorCode=U_ZERO_ERROR;
740 }
741
742 if(subdone) {
743 if(haveLang && haveRest) {
744 /* append internal portion of pattern, the first time,
745 or last portion of pattern the second time */
746 int32_t padLen;
747 patPos+=subLen;
748 padLen=(subi==0 ? sub1Pos : patLen)-patPos;
749 if(length+padLen <= destCapacity) {
750 p=dest+length;
751 for(int32_t i=0;i<padLen;++i) {
752 *p++=pattern[patPos++];
753 }
754 } else {
755 patPos+=padLen;
756 }
757 length+=padLen;
758 } else if(subi==0) {
759 /* don't have first component, reset for second component */
760 sub0Pos=0;
761 length=0;
762 } else if(length>0) {
763 /* true length is the length of just the component we got. */
764 length=haveLang?langLen:restLen;
765 if(dest && sub0Pos!=0) {
766 if (sub0Pos+length<=destCapacity) {
767 /* first component not at start of result,
768 but we have full component in buffer. */
769 u_memmove(dest, dest+(haveLang?langPos:restPos), length);
770 } else {
771 /* would have fit, but didn't because of pattern prefix. */
772 sub0Pos=0; /* stops initial padding (and a second retry,
773 so we won't end up here again) */
774 retry=true;
775 }
776 }
777 }
778
779 ++subi; /* move on to next substitution */
780 }
781 }
782 } while(retry);
783
784 return u_terminateUChars(dest, destCapacity, length, pErrorCode);
785 }
786
787 U_CAPI int32_t U_EXPORT2
uloc_getDisplayKeyword(const char * keyword,const char * displayLocale,char16_t * dest,int32_t destCapacity,UErrorCode * status)788 uloc_getDisplayKeyword(const char* keyword,
789 const char* displayLocale,
790 char16_t* dest,
791 int32_t destCapacity,
792 UErrorCode* status){
793
794 /* argument checking */
795 if(status==nullptr || U_FAILURE(*status)) {
796 return 0;
797 }
798
799 if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) {
800 *status=U_ILLEGAL_ARGUMENT_ERROR;
801 return 0;
802 }
803
804
805 /* pass itemKey=nullptr to look for a top-level item */
806 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale,
807 _kKeys, nullptr,
808 keyword,
809 keyword,
810 dest, destCapacity,
811 *status);
812
813 }
814
815
816 #define UCURRENCY_DISPLAY_NAME_INDEX 1
817
818 U_CAPI int32_t U_EXPORT2
uloc_getDisplayKeywordValue(const char * locale,const char * keyword,const char * displayLocale,char16_t * dest,int32_t destCapacity,UErrorCode * status)819 uloc_getDisplayKeywordValue( const char* locale,
820 const char* keyword,
821 const char* displayLocale,
822 char16_t* dest,
823 int32_t destCapacity,
824 UErrorCode* status){
825
826
827 /* argument checking */
828 if(status==nullptr || U_FAILURE(*status)) {
829 return 0;
830 }
831
832 if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) {
833 *status=U_ILLEGAL_ARGUMENT_ERROR;
834 return 0;
835 }
836
837 /* get the keyword value */
838 CharString keywordValue = ulocimp_getKeywordValue(locale, keyword, *status);
839
840 /*
841 * if the keyword is equal to currency .. then to get the display name
842 * we need to do the fallback ourselves
843 */
844 if(uprv_stricmp(keyword, _kCurrency)==0){
845
846 int32_t dispNameLen = 0;
847 const char16_t *dispName = nullptr;
848
849 icu::LocalUResourceBundlePointer bundle(
850 ures_open(U_ICUDATA_CURR, displayLocale, status));
851 icu::LocalUResourceBundlePointer currencies(
852 ures_getByKey(bundle.getAlias(), _kCurrencies, nullptr, status));
853 icu::LocalUResourceBundlePointer currency(
854 ures_getByKeyWithFallback(currencies.getAlias(), keywordValue.data(), nullptr, status));
855
856 dispName = ures_getStringByIndex(currency.getAlias(), UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status);
857
858 if(U_FAILURE(*status)){
859 if(*status == U_MISSING_RESOURCE_ERROR){
860 /* we just want to write the value over if nothing is available */
861 *status = U_USING_DEFAULT_WARNING;
862 }else{
863 return 0;
864 }
865 }
866
867 /* now copy the dispName over if not nullptr */
868 if(dispName != nullptr){
869 if(dispNameLen <= destCapacity){
870 u_memcpy(dest, dispName, dispNameLen);
871 return u_terminateUChars(dest, destCapacity, dispNameLen, status);
872 }else{
873 *status = U_BUFFER_OVERFLOW_ERROR;
874 return dispNameLen;
875 }
876 }else{
877 /* we have not found the display name for the value .. just copy over */
878 if(keywordValue.length() <= destCapacity){
879 u_charsToUChars(keywordValue.data(), dest, keywordValue.length());
880 return u_terminateUChars(dest, destCapacity, keywordValue.length(), status);
881 }else{
882 *status = U_BUFFER_OVERFLOW_ERROR;
883 return keywordValue.length();
884 }
885 }
886
887
888 }else{
889
890 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale,
891 _kTypes, keyword,
892 keywordValue.data(),
893 keywordValue.data(),
894 dest, destCapacity,
895 *status);
896 }
897 }
898