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