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