]> git.saurik.com Git - apple/icu.git/blame - icuSources/common/locdispnames.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / common / locdispnames.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
729e4ab9
A
3/*
4*******************************************************************************
5*
2ca993e8 6* Copyright (C) 1997-2016, International Business Machines
729e4ab9
A
7* Corporation and others. All Rights Reserved.
8*
9*******************************************************************************
10* file name: locdispnames.cpp
f3c0d7a5 11* encoding: UTF-8
729e4ab9
A
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"
3d1f044b 25#include "unicode/uenum.h"
729e4ab9
A
26#include "unicode/uloc.h"
27#include "unicode/ures.h"
28#include "unicode/ustring.h"
3d1f044b
A
29#include "unicode/uldnames.h"
30#include "charstr.h"
729e4ab9
A
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
41U_NAMESPACE_BEGIN
42
43UnicodeString&
44Locale::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
55UnicodeString&
56Locale::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
89UnicodeString&
90Locale::getDisplayScript(UnicodeString& dispScript) const
91{
92 return this->getDisplayScript(getDefault(), dispScript);
93}
94
95UnicodeString&
96Locale::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
129UnicodeString&
130Locale::getDisplayCountry(UnicodeString& dispCntry) const
131{
132 return this->getDisplayCountry(getDefault(), dispCntry);
133}
134
135UnicodeString&
136Locale::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
169UnicodeString&
170Locale::getDisplayVariant(UnicodeString& dispVar) const
171{
172 return this->getDisplayVariant(getDefault(), dispVar);
173}
174
175UnicodeString&
176Locale::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
209UnicodeString&
210Locale::getDisplayName( UnicodeString& name ) const
211{
212 return this->getDisplayName(getDefault(), name);
213}
214
215UnicodeString&
216Locale::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.
253UnicodeString& U_EXPORT2
254BreakIterator::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.
262UnicodeString& U_EXPORT2
263BreakIterator::getDisplayName(const Locale& objectLocale,
264 const Locale& displayLocale,
265 UnicodeString& name)
266{
267 return objectLocale.getDisplayName(displayLocale, name);
268}
269
270#endif
271
272
273U_NAMESPACE_END
274
275// C API ------------------------------------------------------------------- ***
276
277U_NAMESPACE_USE
278
279/* ### Constants **************************************************/
280
281/* These strings describe the resources we attempt to load from
282 the locale ResourceBundle data file.*/
283static const char _kLanguages[] = "Languages";
284static const char _kScripts[] = "Scripts";
4388f060 285static const char _kScriptsStandAlone[] = "Scripts%stand-alone";
729e4ab9
A
286static const char _kCountries[] = "Countries";
287static const char _kVariants[] = "Variants";
288static const char _kKeys[] = "Keys";
289static const char _kTypes[] = "Types";
51004dcb 290//static const char _kRootName[] = "root";
729e4ab9
A
291static const char _kCurrency[] = "currency";
292static const char _kCurrencies[] = "Currencies";
293static const char _kLocaleDisplayPattern[] = "localeDisplayPattern";
294static const char _kPattern[] = "pattern";
295static const char _kSeparator[] = "separator";
296
297/* ### Display name **************************************************/
298
299static int32_t
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 */
3d1f044b 312 icu::LocalUResourceBundlePointer rb(ures_open(path, locale, pErrorCode));
729e4ab9
A
313
314 if(U_SUCCESS(*pErrorCode)) {
3d1f044b 315 s=ures_getStringByKey(rb.getAlias(), tableKey, &length, pErrorCode);
729e4ab9 316 /* see comment about closing rb near "return item;" in _res_getTableStringWithFallback() */
729e4ab9
A
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
348typedef int32_t U_CALLCONV UDisplayNameGetter(const char *, char *, int32_t, UErrorCode *);
349
350static int32_t
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
391U_CAPI int32_t U_EXPORT2
392uloc_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
400U_CAPI int32_t U_EXPORT2
401uloc_getDisplayScript(const char* locale,
402 const char* displayLocale,
403 UChar *dest, int32_t destCapacity,
404 UErrorCode *pErrorCode)
4388f060
A
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
419U_INTERNAL int32_t U_EXPORT2
420uloc_getDisplayScriptInContext(const char* locale,
421 const char* displayLocale,
422 UChar *dest, int32_t destCapacity,
423 UErrorCode *pErrorCode)
729e4ab9
A
424{
425 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity,
4388f060 426 uloc_getScript, _kScripts, pErrorCode);
729e4ab9
A
427}
428
429U_CAPI int32_t U_EXPORT2
430uloc_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 */
443U_CAPI int32_t U_EXPORT2
444uloc_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
4388f060
A
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 */
729e4ab9
A
464U_CAPI int32_t U_EXPORT2
465uloc_getDisplayName(const char *locale,
466 const char *displayLocale,
467 UChar *dest, int32_t destCapacity,
468 UErrorCode *pErrorCode)
469{
57a6839d 470 static const UChar defaultSeparator[9] = { 0x007b, 0x0030, 0x007d, 0x002c, 0x0020, 0x007b, 0x0031, 0x007d, 0x0000 }; /* "{0}, {1}" */
4388f060
A
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;
57a6839d
A
488
489 UChar formatOpenParen = 0x0028; // (
490 UChar formatReplaceOpenParen = 0x005B; // [
491 UChar formatCloseParen = 0x0029; // )
492 UChar formatReplaceCloseParen = 0x005D; // ]
4388f060
A
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 */
729e4ab9 501
729e4ab9
A
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
3d1f044b
A
511 { // For select languages + script, route to uldn_localeDisplayName <rdar://problem/51418203>
512 char lang[ULOC_LANG_CAPACITY];
513 char script[ULOC_SCRIPT_CAPACITY ];
514 UErrorCode status = U_ZERO_ERROR;
515 int32_t langlen = uloc_getLanguage(locale, lang, ULOC_LANG_CAPACITY, &status);
516 int32_t scriptlen = uloc_getScript(locale, script, ULOC_SCRIPT_CAPACITY, &status);
517 if ( U_SUCCESS(status) && scriptlen>0 && (uprv_strcmp(lang, "zh") == 0 || uprv_strcmp(lang, "yue") == 0 ||
518 uprv_strcmp(lang, "ks") == 0 || uprv_strcmp(lang, "pa") == 0 || uprv_strcmp(lang, "ur") == 0) ) {
519 ULocaleDisplayNames* uldn = uldn_open(displayLocale, ULDN_STANDARD_NAMES, &status);
520 if ( U_SUCCESS(status) ) {
521 int32_t namelen = uldn_localeDisplayName(uldn, locale, dest, destCapacity, pErrorCode);
522 uldn_close(uldn);
523 return namelen;
524 }
525 }
526 }
527
4388f060
A
528 {
529 UErrorCode status = U_ZERO_ERROR;
729e4ab9 530
3d1f044b
A
531 icu::LocalUResourceBundlePointer locbundle(
532 ures_open(U_ICUDATA_LANG, displayLocale, &status));
533 icu::LocalUResourceBundlePointer dspbundle(
534 ures_getByKeyWithFallback(locbundle.getAlias(), _kLocaleDisplayPattern, NULL, &status));
729e4ab9 535
3d1f044b
A
536 separator=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kSeparator, &sepLen, &status);
537 pattern=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kPattern, &patLen, &status);
729e4ab9
A
538 }
539
4388f060
A
540 /* If we couldn't find any data, then use the defaults */
541 if(sepLen == 0) {
542 separator = defaultSeparator;
57a6839d
A
543 }
544 /* #10244: Even though separator is now a pattern, it is awkward to handle it as such
545 * here since we are trying to build the display string in place in the dest buffer,
546 * and to handle it as a pattern would entail having separate storage for the
547 * substrings that need to be combined (the first of which may be the result of
548 * previous such combinations). So for now we continue to treat the portion between
549 * {0} and {1} as a string to be appended when joining substrings, ignoring anything
550 * that is before {0} or after {1} (no existing separator pattern has any such thing).
551 * This is similar to how pattern is handled below.
552 */
553 {
554 UChar *p0=u_strstr(separator, sub0);
555 UChar *p1=u_strstr(separator, sub1);
556 if (p0==NULL || p1==NULL || p1<p0) {
557 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
558 return 0;
559 }
560 separator = (const UChar *)p0 + subLen;
0f5d89e8 561 sepLen = static_cast<int32_t>(p1 - separator);
4388f060
A
562 }
563
564 if(patLen==0 || (patLen==defaultPatLen && !u_strncmp(pattern, defaultPattern, patLen))) {
565 pattern=defaultPattern;
566 patLen=defaultPatLen;
567 sub0Pos=defaultSub0Pos;
568 sub1Pos=defaultSub1Pos;
57a6839d 569 // use default formatOpenParen etc. set above
4388f060
A
570 } else { /* non-default pattern */
571 UChar *p0=u_strstr(pattern, sub0);
572 UChar *p1=u_strstr(pattern, sub1);
573 if (p0==NULL || p1==NULL) {
574 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
575 return 0;
729e4ab9 576 }
0f5d89e8
A
577 sub0Pos = static_cast<int32_t>(p0-pattern);
578 sub1Pos = static_cast<int32_t>(p1-pattern);
4388f060
A
579 if (sub1Pos < sub0Pos) { /* a very odd pattern */
580 int32_t t=sub0Pos; sub0Pos=sub1Pos; sub1Pos=t;
581 langi=1;
729e4ab9 582 }
57a6839d
A
583 if (u_strchr(pattern, 0xFF08) != NULL) {
584 formatOpenParen = 0xFF08; // fullwidth (
585 formatReplaceOpenParen = 0xFF3B; // fullwidth [
586 formatCloseParen = 0xFF09; // fullwidth )
587 formatReplaceCloseParen = 0xFF3D; // fullwidth ]
588 }
729e4ab9 589 }
729e4ab9 590
4388f060
A
591 /* We loop here because there is one case in which after the first pass we could need to
592 * reextract the data. If there's initial padding before the first element, we put in
593 * the padding and then write that element. If it turns out there's no second element,
594 * we didn't need the padding. If we do need the data (no preflight), and the first element
595 * would have fit but for the padding, we need to reextract. In this case (only) we
596 * adjust the parameters so padding is not added, and repeat.
597 */
598 do {
599 UChar* p=dest;
600 int32_t patPos=0; /* position in the pattern, used for non-substitution portions */
601 int32_t langLen=0; /* length of language substitution */
602 int32_t langPos=0; /* position in output of language substitution */
603 int32_t restLen=0; /* length of 'everything else' substitution */
604 int32_t restPos=0; /* position in output of 'everything else' substitution */
3d1f044b 605 icu::LocalUEnumerationPointer kenum; /* keyword enumeration */
4388f060
A
606
607 /* prefix of pattern, extremely likely to be empty */
608 if(sub0Pos) {
609 if(destCapacity >= sub0Pos) {
610 while (patPos < sub0Pos) {
611 *p++ = pattern[patPos++];
612 }
613 } else {
614 patPos=sub0Pos;
615 }
616 length=sub0Pos;
617 } else {
618 length=0;
729e4ab9 619 }
729e4ab9 620
4388f060
A
621 for(int32_t subi=0,resti=0;subi<2;) { /* iterate through patterns 0 and 1*/
622 UBool subdone = FALSE; /* set true when ready to move to next substitution */
729e4ab9 623
4388f060
A
624 /* prep p and cap for calls to get display components, pin cap to 0 since
625 they complain if cap is negative */
626 int32_t cap=destCapacity-length;
627 if (cap <= 0) {
628 cap=0;
729e4ab9 629 } else {
4388f060 630 p=dest+length;
729e4ab9 631 }
729e4ab9 632
4388f060
A
633 if (subi == langi) { /* {0}*/
634 if(haveLang) {
635 langPos=length;
636 langLen=uloc_getDisplayLanguage(locale, displayLocale, p, cap, pErrorCode);
637 length+=langLen;
638 haveLang=langLen>0;
639 }
640 subdone=TRUE;
641 } else { /* {1} */
642 if(!haveRest) {
643 subdone=TRUE;
644 } else {
645 int32_t len; /* length of component (plus other stuff) we just fetched */
646 switch(resti++) {
647 case 0:
648 restPos=length;
649 len=uloc_getDisplayScriptInContext(locale, displayLocale, p, cap, pErrorCode);
650 break;
651 case 1:
652 len=uloc_getDisplayCountry(locale, displayLocale, p, cap, pErrorCode);
653 break;
654 case 2:
655 len=uloc_getDisplayVariant(locale, displayLocale, p, cap, pErrorCode);
656 break;
657 case 3:
3d1f044b 658 kenum.adoptInstead(uloc_openKeywords(locale, pErrorCode));
2ca993e8 659 U_FALLTHROUGH;
4388f060 660 default: {
3d1f044b 661 const char* kw=uenum_next(kenum.getAlias(), &len, pErrorCode);
4388f060 662 if (kw == NULL) {
4388f060
A
663 len=0; /* mark that we didn't add a component */
664 subdone=TRUE;
665 } else {
666 /* incorporating this behavior into the loop made it even more complex,
667 so just special case it here */
668 len = uloc_getDisplayKeyword(kw, displayLocale, p, cap, pErrorCode);
669 if(len) {
670 if(len < cap) {
671 p[len]=0x3d; /* '=', assume we'll need it */
672 }
673 len+=1;
674
675 /* adjust for call to get keyword */
676 cap-=len;
677 if(cap <= 0) {
678 cap=0;
679 } else {
680 p+=len;
681 }
682 }
683 /* reset for call below */
684 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) {
685 *pErrorCode=U_ZERO_ERROR;
686 }
687 int32_t vlen = uloc_getDisplayKeywordValue(locale, kw, displayLocale,
688 p, cap, pErrorCode);
689 if(len) {
690 if(vlen==0) {
691 --len; /* remove unneeded '=' */
692 }
693 /* restore cap and p to what they were at start */
694 cap=destCapacity-length;
695 if(cap <= 0) {
696 cap=0;
697 } else {
698 p=dest+length;
699 }
700 }
701 len+=vlen; /* total we added for key + '=' + value */
702 }
703 } break;
704 } /* end switch */
705
706 if (len>0) {
707 /* we addeed a component, so add separator and write it if there's room. */
708 if(len+sepLen<=cap) {
57a6839d
A
709 const UChar * plimit = p + len;
710 for (; p < plimit; p++) {
711 if (*p == formatOpenParen) {
712 *p = formatReplaceOpenParen;
713 } else if (*p == formatCloseParen) {
714 *p = formatReplaceCloseParen;
715 }
716 }
4388f060
A
717 for(int32_t i=0;i<sepLen;++i) {
718 *p++=separator[i];
719 }
720 }
721 length+=len+sepLen;
722 } else if(subdone) {
723 /* remove separator if we added it */
724 if (length!=restPos) {
725 length-=sepLen;
726 }
727 restLen=length-restPos;
728 haveRest=restLen>0;
729 }
730 }
731 }
729e4ab9 732
4388f060
A
733 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) {
734 *pErrorCode=U_ZERO_ERROR;
735 }
729e4ab9 736
4388f060
A
737 if(subdone) {
738 if(haveLang && haveRest) {
739 /* append internal portion of pattern, the first time,
740 or last portion of pattern the second time */
741 int32_t padLen;
742 patPos+=subLen;
743 padLen=(subi==0 ? sub1Pos : patLen)-patPos;
744 if(length+padLen < destCapacity) {
745 p=dest+length;
746 for(int32_t i=0;i<padLen;++i) {
747 *p++=pattern[patPos++];
748 }
749 } else {
750 patPos+=padLen;
751 }
752 length+=padLen;
753 } else if(subi==0) {
754 /* don't have first component, reset for second component */
755 sub0Pos=0;
756 length=0;
757 } else if(length>0) {
758 /* true length is the length of just the component we got. */
759 length=haveLang?langLen:restLen;
760 if(dest && sub0Pos!=0) {
761 if (sub0Pos+length<=destCapacity) {
762 /* first component not at start of result,
763 but we have full component in buffer. */
764 u_memmove(dest, dest+(haveLang?langPos:restPos), length);
765 } else {
766 /* would have fit, but didn't because of pattern prefix. */
767 sub0Pos=0; /* stops initial padding (and a second retry,
768 so we won't end up here again) */
769 retry=TRUE;
770 }
771 }
772 }
773
774 ++subi; /* move on to next substitution */
775 }
729e4ab9 776 }
4388f060 777 } while(retry);
729e4ab9
A
778
779 return u_terminateUChars(dest, destCapacity, length, pErrorCode);
780}
781
782U_CAPI int32_t U_EXPORT2
783uloc_getDisplayKeyword(const char* keyword,
784 const char* displayLocale,
785 UChar* dest,
786 int32_t destCapacity,
787 UErrorCode* status){
788
789 /* argument checking */
790 if(status==NULL || U_FAILURE(*status)) {
791 return 0;
792 }
793
794 if(destCapacity<0 || (destCapacity>0 && dest==NULL)) {
795 *status=U_ILLEGAL_ARGUMENT_ERROR;
796 return 0;
797 }
798
799
800 /* pass itemKey=NULL to look for a top-level item */
801 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale,
802 _kKeys, NULL,
803 keyword,
804 keyword,
805 dest, destCapacity,
806 status);
807
808}
809
810
811#define UCURRENCY_DISPLAY_NAME_INDEX 1
812
813U_CAPI int32_t U_EXPORT2
814uloc_getDisplayKeywordValue( const char* locale,
815 const char* keyword,
816 const char* displayLocale,
817 UChar* dest,
818 int32_t destCapacity,
819 UErrorCode* status){
820
821
822 char keywordValue[ULOC_FULLNAME_CAPACITY*4];
823 int32_t capacity = ULOC_FULLNAME_CAPACITY*4;
824 int32_t keywordValueLen =0;
825
826 /* argument checking */
827 if(status==NULL || U_FAILURE(*status)) {
828 return 0;
829 }
830
831 if(destCapacity<0 || (destCapacity>0 && dest==NULL)) {
832 *status=U_ILLEGAL_ARGUMENT_ERROR;
833 return 0;
834 }
835
836 /* get the keyword value */
837 keywordValue[0]=0;
838 keywordValueLen = uloc_getKeywordValue(locale, keyword, keywordValue, capacity, status);
0f5d89e8
A
839 if (*status == U_STRING_NOT_TERMINATED_WARNING)
840 *status = U_BUFFER_OVERFLOW_ERROR;
729e4ab9
A
841
842 /*
843 * if the keyword is equal to currency .. then to get the display name
844 * we need to do the fallback ourselves
845 */
846 if(uprv_stricmp(keyword, _kCurrency)==0){
847
848 int32_t dispNameLen = 0;
849 const UChar *dispName = NULL;
3d1f044b
A
850
851 icu::LocalUResourceBundlePointer bundle(
852 ures_open(U_ICUDATA_CURR, displayLocale, status));
853 icu::LocalUResourceBundlePointer currencies(
854 ures_getByKey(bundle.getAlias(), _kCurrencies, NULL, status));
855 icu::LocalUResourceBundlePointer currency(
856 ures_getByKeyWithFallback(currencies.getAlias(), keywordValue, NULL, status));
857
858 dispName = ures_getStringByIndex(currency.getAlias(), UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status);
859
729e4ab9
A
860 if(U_FAILURE(*status)){
861 if(*status == U_MISSING_RESOURCE_ERROR){
862 /* we just want to write the value over if nothing is available */
863 *status = U_USING_DEFAULT_WARNING;
864 }else{
865 return 0;
866 }
867 }
868
869 /* now copy the dispName over if not NULL */
870 if(dispName != NULL){
871 if(dispNameLen <= destCapacity){
a62d09fc 872 u_memcpy(dest, dispName, dispNameLen);
729e4ab9
A
873 return u_terminateUChars(dest, destCapacity, dispNameLen, status);
874 }else{
875 *status = U_BUFFER_OVERFLOW_ERROR;
876 return dispNameLen;
877 }
878 }else{
879 /* we have not found the display name for the value .. just copy over */
880 if(keywordValueLen <= destCapacity){
881 u_charsToUChars(keywordValue, dest, keywordValueLen);
882 return u_terminateUChars(dest, destCapacity, keywordValueLen, status);
883 }else{
884 *status = U_BUFFER_OVERFLOW_ERROR;
885 return keywordValueLen;
886 }
887 }
888
889
890 }else{
891
892 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale,
893 _kTypes, keyword,
894 keywordValue,
895 keywordValue,
896 dest, destCapacity,
897 status);
898 }
899}