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