Coverage Report

Created: 2025-11-07 06:50

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