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 /* 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 // For the display name, we treat this as unknown language (ICU-20273).
380 if (getter == uloc_getLanguage) {
381 uprv_strcpy(localeBuffer, "und");
382 } else {
383 return u_terminateUChars(dest, destCapacity, 0, pErrorCode);
384 }
385 }
386
387 root = tag == _kCountries ? U_ICUDATA_REGION : U_ICUDATA_LANG;
388
389 return _getStringOrCopyKey(root, displayLocale,
390 tag, NULL, localeBuffer,
391 localeBuffer,
392 dest, destCapacity,
393 pErrorCode);
394 }
395
396 U_CAPI int32_t U_EXPORT2
uloc_getDisplayLanguage(const char * locale,const char * displayLocale,UChar * dest,int32_t destCapacity,UErrorCode * pErrorCode)397 uloc_getDisplayLanguage(const char *locale,
398 const char *displayLocale,
399 UChar *dest, int32_t destCapacity,
400 UErrorCode *pErrorCode) {
401 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
402 uloc_getLanguage, _kLanguages, pErrorCode);
403 }
404
405 U_CAPI int32_t U_EXPORT2
uloc_getDisplayScript(const char * locale,const char * displayLocale,UChar * dest,int32_t destCapacity,UErrorCode * pErrorCode)406 uloc_getDisplayScript(const char* locale,
407 const char* displayLocale,
408 UChar *dest, int32_t destCapacity,
409 UErrorCode *pErrorCode)
410 {
411 UErrorCode err = U_ZERO_ERROR;
412 int32_t res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
413 uloc_getScript, _kScriptsStandAlone, &err);
414
415 if (destCapacity == 0 && err == U_BUFFER_OVERFLOW_ERROR) {
416 // For preflight, return the max of the value and the fallback.
417 int32_t fallback_res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
418 uloc_getScript, _kScripts, pErrorCode);
419 return (fallback_res > res) ? fallback_res : res;
420 }
421 if ( err == U_USING_DEFAULT_WARNING ) {
422 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
423 uloc_getScript, _kScripts, pErrorCode);
424 } else {
425 *pErrorCode = err;
426 return res;
427 }
428 }
429
430 static int32_t
uloc_getDisplayScriptInContext(const char * locale,const char * displayLocale,UChar * dest,int32_t destCapacity,UErrorCode * pErrorCode)431 uloc_getDisplayScriptInContext(const char* locale,
432 const char* displayLocale,
433 UChar *dest, int32_t destCapacity,
434 UErrorCode *pErrorCode)
435 {
436 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
437 uloc_getScript, _kScripts, pErrorCode);
438 }
439
440 U_CAPI int32_t U_EXPORT2
uloc_getDisplayCountry(const char * locale,const char * displayLocale,UChar * dest,int32_t destCapacity,UErrorCode * pErrorCode)441 uloc_getDisplayCountry(const char *locale,
442 const char *displayLocale,
443 UChar *dest, int32_t destCapacity,
444 UErrorCode *pErrorCode) {
445 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
446 uloc_getCountry, _kCountries, pErrorCode);
447 }
448
449 /*
450 * TODO separate variant1_variant2_variant3...
451 * by getting each tag's display string and concatenating them with ", "
452 * in between - similar to uloc_getDisplayName()
453 */
454 U_CAPI int32_t U_EXPORT2
uloc_getDisplayVariant(const char * locale,const char * displayLocale,UChar * dest,int32_t destCapacity,UErrorCode * pErrorCode)455 uloc_getDisplayVariant(const char *locale,
456 const char *displayLocale,
457 UChar *dest, int32_t destCapacity,
458 UErrorCode *pErrorCode) {
459 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
460 uloc_getVariant, _kVariants, pErrorCode);
461 }
462
463 /* Instead of having a separate pass for 'special' patterns, reintegrate the two
464 * so we don't get bitten by preflight bugs again. We can be reasonably efficient
465 * without two separate code paths, this code isn't that performance-critical.
466 *
467 * This code is general enough to deal with patterns that have a prefix or swap the
468 * language and remainder components, since we gave developers enough rope to do such
469 * things if they futz with the pattern data. But since we don't give them a way to
470 * specify a pattern for arbitrary combinations of components, there's not much use in
471 * that. I don't think our data includes such patterns, the only variable I know if is
472 * whether there is a space before the open paren, or not. Oh, and zh uses different
473 * chars than the standard open/close paren (which ja and ko use, btw).
474 */
475 U_CAPI int32_t U_EXPORT2
uloc_getDisplayName(const char * locale,const char * displayLocale,UChar * dest,int32_t destCapacity,UErrorCode * pErrorCode)476 uloc_getDisplayName(const char *locale,
477 const char *displayLocale,
478 UChar *dest, int32_t destCapacity,
479 UErrorCode *pErrorCode)
480 {
481 static const UChar defaultSeparator[9] = { 0x007b, 0x0030, 0x007d, 0x002c, 0x0020, 0x007b, 0x0031, 0x007d, 0x0000 }; /* "{0}, {1}" */
482 static const UChar sub0[4] = { 0x007b, 0x0030, 0x007d , 0x0000 } ; /* {0} */
483 static const UChar sub1[4] = { 0x007b, 0x0031, 0x007d , 0x0000 } ; /* {1} */
484 static const int32_t subLen = 3;
485 static const UChar defaultPattern[10] = {
486 0x007b, 0x0030, 0x007d, 0x0020, 0x0028, 0x007b, 0x0031, 0x007d, 0x0029, 0x0000
487 }; /* {0} ({1}) */
488 static const int32_t defaultPatLen = 9;
489 static const int32_t defaultSub0Pos = 0;
490 static const int32_t defaultSub1Pos = 5;
491
492 int32_t length; /* of formatted result */
493
494 const UChar *separator;
495 int32_t sepLen = 0;
496 const UChar *pattern;
497 int32_t patLen = 0;
498 int32_t sub0Pos, sub1Pos;
499
500 UChar formatOpenParen = 0x0028; // (
501 UChar formatReplaceOpenParen = 0x005B; // [
502 UChar formatCloseParen = 0x0029; // )
503 UChar formatReplaceCloseParen = 0x005D; // ]
504
505 UBool haveLang = TRUE; /* assume true, set false if we find we don't have
506 a lang component in the locale */
507 UBool haveRest = TRUE; /* assume true, set false if we find we don't have
508 any other component in the locale */
509 UBool retry = FALSE; /* set true if we need to retry, see below */
510
511 int32_t langi = 0; /* index of the language substitution (0 or 1), virtually always 0 */
512
513 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
514 return 0;
515 }
516
517 if(destCapacity<0 || (destCapacity>0 && dest==NULL)) {
518 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
519 return 0;
520 }
521
522 {
523 UErrorCode status = U_ZERO_ERROR;
524
525 icu::LocalUResourceBundlePointer locbundle(
526 ures_open(U_ICUDATA_LANG, displayLocale, &status));
527 icu::LocalUResourceBundlePointer dspbundle(
528 ures_getByKeyWithFallback(locbundle.getAlias(), _kLocaleDisplayPattern, NULL, &status));
529
530 separator=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kSeparator, &sepLen, &status);
531 pattern=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kPattern, &patLen, &status);
532 }
533
534 /* If we couldn't find any data, then use the defaults */
535 if(sepLen == 0) {
536 separator = defaultSeparator;
537 }
538 /* #10244: Even though separator is now a pattern, it is awkward to handle it as such
539 * here since we are trying to build the display string in place in the dest buffer,
540 * and to handle it as a pattern would entail having separate storage for the
541 * substrings that need to be combined (the first of which may be the result of
542 * previous such combinations). So for now we continue to treat the portion between
543 * {0} and {1} as a string to be appended when joining substrings, ignoring anything
544 * that is before {0} or after {1} (no existing separator pattern has any such thing).
545 * This is similar to how pattern is handled below.
546 */
547 {
548 UChar *p0=u_strstr(separator, sub0);
549 UChar *p1=u_strstr(separator, sub1);
550 if (p0==NULL || p1==NULL || p1<p0) {
551 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
552 return 0;
553 }
554 separator = (const UChar *)p0 + subLen;
555 sepLen = static_cast<int32_t>(p1 - separator);
556 }
557
558 if(patLen==0 || (patLen==defaultPatLen && !u_strncmp(pattern, defaultPattern, patLen))) {
559 pattern=defaultPattern;
560 patLen=defaultPatLen;
561 sub0Pos=defaultSub0Pos;
562 sub1Pos=defaultSub1Pos;
563 // use default formatOpenParen etc. set above
564 } else { /* non-default pattern */
565 UChar *p0=u_strstr(pattern, sub0);
566 UChar *p1=u_strstr(pattern, sub1);
567 if (p0==NULL || p1==NULL) {
568 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
569 return 0;
570 }
571 sub0Pos = static_cast<int32_t>(p0-pattern);
572 sub1Pos = static_cast<int32_t>(p1-pattern);
573 if (sub1Pos < sub0Pos) { /* a very odd pattern */
574 int32_t t=sub0Pos; sub0Pos=sub1Pos; sub1Pos=t;
575 langi=1;
576 }
577 if (u_strchr(pattern, 0xFF08) != NULL) {
578 formatOpenParen = 0xFF08; // fullwidth (
579 formatReplaceOpenParen = 0xFF3B; // fullwidth [
580 formatCloseParen = 0xFF09; // fullwidth )
581 formatReplaceCloseParen = 0xFF3D; // fullwidth ]
582 }
583 }
584
585 /* We loop here because there is one case in which after the first pass we could need to
586 * reextract the data. If there's initial padding before the first element, we put in
587 * the padding and then write that element. If it turns out there's no second element,
588 * we didn't need the padding. If we do need the data (no preflight), and the first element
589 * would have fit but for the padding, we need to reextract. In this case (only) we
590 * adjust the parameters so padding is not added, and repeat.
591 */
592 do {
593 UChar* p=dest;
594 int32_t patPos=0; /* position in the pattern, used for non-substitution portions */
595 int32_t langLen=0; /* length of language substitution */
596 int32_t langPos=0; /* position in output of language substitution */
597 int32_t restLen=0; /* length of 'everything else' substitution */
598 int32_t restPos=0; /* position in output of 'everything else' substitution */
599 icu::LocalUEnumerationPointer kenum; /* keyword enumeration */
600
601 /* prefix of pattern, extremely likely to be empty */
602 if(sub0Pos) {
603 if(destCapacity >= sub0Pos) {
604 while (patPos < sub0Pos) {
605 *p++ = pattern[patPos++];
606 }
607 } else {
608 patPos=sub0Pos;
609 }
610 length=sub0Pos;
611 } else {
612 length=0;
613 }
614
615 for(int32_t subi=0,resti=0;subi<2;) { /* iterate through patterns 0 and 1*/
616 UBool subdone = FALSE; /* set true when ready to move to next substitution */
617
618 /* prep p and cap for calls to get display components, pin cap to 0 since
619 they complain if cap is negative */
620 int32_t cap=destCapacity-length;
621 if (cap <= 0) {
622 cap=0;
623 } else {
624 p=dest+length;
625 }
626
627 if (subi == langi) { /* {0}*/
628 if(haveLang) {
629 langPos=length;
630 langLen=uloc_getDisplayLanguage(locale, displayLocale, p, cap, pErrorCode);
631 length+=langLen;
632 haveLang=langLen>0;
633 }
634 subdone=TRUE;
635 } else { /* {1} */
636 if(!haveRest) {
637 subdone=TRUE;
638 } else {
639 int32_t len; /* length of component (plus other stuff) we just fetched */
640 switch(resti++) {
641 case 0:
642 restPos=length;
643 len=uloc_getDisplayScriptInContext(locale, displayLocale, p, cap, pErrorCode);
644 break;
645 case 1:
646 len=uloc_getDisplayCountry(locale, displayLocale, p, cap, pErrorCode);
647 break;
648 case 2:
649 len=uloc_getDisplayVariant(locale, displayLocale, p, cap, pErrorCode);
650 break;
651 case 3:
652 kenum.adoptInstead(uloc_openKeywords(locale, pErrorCode));
653 U_FALLTHROUGH;
654 default: {
655 const char* kw=uenum_next(kenum.getAlias(), &len, pErrorCode);
656 if (kw == NULL) {
657 len=0; /* mark that we didn't add a component */
658 subdone=TRUE;
659 } else {
660 /* incorporating this behavior into the loop made it even more complex,
661 so just special case it here */
662 len = uloc_getDisplayKeyword(kw, displayLocale, p, cap, pErrorCode);
663 if(len) {
664 if(len < cap) {
665 p[len]=0x3d; /* '=', assume we'll need it */
666 }
667 len+=1;
668
669 /* adjust for call to get keyword */
670 cap-=len;
671 if(cap <= 0) {
672 cap=0;
673 } else {
674 p+=len;
675 }
676 }
677 /* reset for call below */
678 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) {
679 *pErrorCode=U_ZERO_ERROR;
680 }
681 int32_t vlen = uloc_getDisplayKeywordValue(locale, kw, displayLocale,
682 p, cap, pErrorCode);
683 if(len) {
684 if(vlen==0) {
685 --len; /* remove unneeded '=' */
686 }
687 /* restore cap and p to what they were at start */
688 cap=destCapacity-length;
689 if(cap <= 0) {
690 cap=0;
691 } else {
692 p=dest+length;
693 }
694 }
695 len+=vlen; /* total we added for key + '=' + value */
696 }
697 } break;
698 } /* end switch */
699
700 if (len>0) {
701 /* we added a component, so add separator and write it if there's room. */
702 if(len+sepLen<=cap) {
703 const UChar * plimit = p + len;
704 for (; p < plimit; p++) {
705 if (*p == formatOpenParen) {
706 *p = formatReplaceOpenParen;
707 } else if (*p == formatCloseParen) {
708 *p = formatReplaceCloseParen;
709 }
710 }
711 for(int32_t i=0;i<sepLen;++i) {
712 *p++=separator[i];
713 }
714 }
715 length+=len+sepLen;
716 } else if(subdone) {
717 /* remove separator if we added it */
718 if (length!=restPos) {
719 length-=sepLen;
720 }
721 restLen=length-restPos;
722 haveRest=restLen>0;
723 }
724 }
725 }
726
727 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) {
728 *pErrorCode=U_ZERO_ERROR;
729 }
730
731 if(subdone) {
732 if(haveLang && haveRest) {
733 /* append internal portion of pattern, the first time,
734 or last portion of pattern the second time */
735 int32_t padLen;
736 patPos+=subLen;
737 padLen=(subi==0 ? sub1Pos : patLen)-patPos;
738 if(length+padLen <= destCapacity) {
739 p=dest+length;
740 for(int32_t i=0;i<padLen;++i) {
741 *p++=pattern[patPos++];
742 }
743 } else {
744 patPos+=padLen;
745 }
746 length+=padLen;
747 } else if(subi==0) {
748 /* don't have first component, reset for second component */
749 sub0Pos=0;
750 length=0;
751 } else if(length>0) {
752 /* true length is the length of just the component we got. */
753 length=haveLang?langLen:restLen;
754 if(dest && sub0Pos!=0) {
755 if (sub0Pos+length<=destCapacity) {
756 /* first component not at start of result,
757 but we have full component in buffer. */
758 u_memmove(dest, dest+(haveLang?langPos:restPos), length);
759 } else {
760 /* would have fit, but didn't because of pattern prefix. */
761 sub0Pos=0; /* stops initial padding (and a second retry,
762 so we won't end up here again) */
763 retry=TRUE;
764 }
765 }
766 }
767
768 ++subi; /* move on to next substitution */
769 }
770 }
771 } while(retry);
772
773 return u_terminateUChars(dest, destCapacity, length, pErrorCode);
774 }
775
776 U_CAPI int32_t U_EXPORT2
uloc_getDisplayKeyword(const char * keyword,const char * displayLocale,UChar * dest,int32_t destCapacity,UErrorCode * status)777 uloc_getDisplayKeyword(const char* keyword,
778 const char* displayLocale,
779 UChar* dest,
780 int32_t destCapacity,
781 UErrorCode* status){
782
783 /* argument checking */
784 if(status==NULL || U_FAILURE(*status)) {
785 return 0;
786 }
787
788 if(destCapacity<0 || (destCapacity>0 && dest==NULL)) {
789 *status=U_ILLEGAL_ARGUMENT_ERROR;
790 return 0;
791 }
792
793
794 /* pass itemKey=NULL to look for a top-level item */
795 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale,
796 _kKeys, NULL,
797 keyword,
798 keyword,
799 dest, destCapacity,
800 status);
801
802 }
803
804
805 #define UCURRENCY_DISPLAY_NAME_INDEX 1
806
807 U_CAPI int32_t U_EXPORT2
uloc_getDisplayKeywordValue(const char * locale,const char * keyword,const char * displayLocale,UChar * dest,int32_t destCapacity,UErrorCode * status)808 uloc_getDisplayKeywordValue( const char* locale,
809 const char* keyword,
810 const char* displayLocale,
811 UChar* dest,
812 int32_t destCapacity,
813 UErrorCode* status){
814
815
816 /* argument checking */
817 if(status==NULL || U_FAILURE(*status)) {
818 return 0;
819 }
820
821 if(destCapacity<0 || (destCapacity>0 && dest==NULL)) {
822 *status=U_ILLEGAL_ARGUMENT_ERROR;
823 return 0;
824 }
825
826 /* get the keyword value */
827 CharString keywordValue;
828 {
829 CharStringByteSink sink(&keywordValue);
830 ulocimp_getKeywordValue(locale, keyword, sink, status);
831 }
832
833 /*
834 * if the keyword is equal to currency .. then to get the display name
835 * we need to do the fallback ourselves
836 */
837 if(uprv_stricmp(keyword, _kCurrency)==0){
838
839 int32_t dispNameLen = 0;
840 const UChar *dispName = NULL;
841
842 icu::LocalUResourceBundlePointer bundle(
843 ures_open(U_ICUDATA_CURR, displayLocale, status));
844 icu::LocalUResourceBundlePointer currencies(
845 ures_getByKey(bundle.getAlias(), _kCurrencies, NULL, status));
846 icu::LocalUResourceBundlePointer currency(
847 ures_getByKeyWithFallback(currencies.getAlias(), keywordValue.data(), NULL, status));
848
849 dispName = ures_getStringByIndex(currency.getAlias(), UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status);
850
851 if(U_FAILURE(*status)){
852 if(*status == U_MISSING_RESOURCE_ERROR){
853 /* we just want to write the value over if nothing is available */
854 *status = U_USING_DEFAULT_WARNING;
855 }else{
856 return 0;
857 }
858 }
859
860 /* now copy the dispName over if not NULL */
861 if(dispName != NULL){
862 if(dispNameLen <= destCapacity){
863 u_memcpy(dest, dispName, dispNameLen);
864 return u_terminateUChars(dest, destCapacity, dispNameLen, status);
865 }else{
866 *status = U_BUFFER_OVERFLOW_ERROR;
867 return dispNameLen;
868 }
869 }else{
870 /* we have not found the display name for the value .. just copy over */
871 if(keywordValue.length() <= destCapacity){
872 u_charsToUChars(keywordValue.data(), dest, keywordValue.length());
873 return u_terminateUChars(dest, destCapacity, keywordValue.length(), status);
874 }else{
875 *status = U_BUFFER_OVERFLOW_ERROR;
876 return keywordValue.length();
877 }
878 }
879
880
881 }else{
882
883 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale,
884 _kTypes, keyword,
885 keywordValue.data(),
886 keywordValue.data(),
887 dest, destCapacity,
888 status);
889 }
890 }
891