• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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