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