Coverage Report

Created: 2026-08-14 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/common/locavailable.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-2013, International Business Machines
7
*   Corporation and others.  All Rights Reserved.
8
*
9
*******************************************************************************
10
*   file name:  locavailable.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 available locales, separated out from other .cpp files
19
*   that then do not depend on resource bundle code and res_index bundles.
20
*/
21
22
#include "unicode/errorcode.h"
23
#include "unicode/utypes.h"
24
#include "unicode/locid.h"
25
#include "unicode/uloc.h"
26
#include "unicode/ures.h"
27
#include "cmemory.h"
28
#include "cstring.h"
29
#include "ucln_cmn.h"
30
#include "uassert.h"
31
#include "umutex.h"
32
#include "uresimp.h"
33
34
// C++ API ----------------------------------------------------------------- ***
35
36
U_NAMESPACE_BEGIN
37
38
static icu::Locale*  availableLocaleList = nullptr;
39
static int32_t  availableLocaleListCount;
40
static icu::UInitOnce gInitOnceLocale {};
41
42
namespace {
43
44
UBool U_CALLCONV locale_available_cleanup()
45
0
{
46
0
    if (availableLocaleList) {
47
0
        delete []availableLocaleList;
48
0
        availableLocaleList = nullptr;
49
0
    }
50
0
    availableLocaleListCount = 0;
51
0
    gInitOnceLocale.reset();
52
53
0
    return true;
54
0
}
55
56
}  // namespace
57
58
10
void U_CALLCONV locale_available_init() {
59
    // This function is a friend of class Locale.
60
    // This function is only invoked via umtx_initOnce().
61
    
62
    // for now, there is a hardcoded list, so just walk through that list and set it up.
63
    //  Note: this function is a friend of class Locale.
64
10
    availableLocaleListCount = uloc_countAvailable();
65
10
    if(availableLocaleListCount) {
66
10
       availableLocaleList = new Locale[availableLocaleListCount];
67
10
    }
68
10
    if (availableLocaleList == nullptr) {
69
0
        availableLocaleListCount= 0;
70
0
    }
71
9.07k
    for (int32_t locCount=availableLocaleListCount-1; locCount>=0; --locCount) {
72
9.06k
        availableLocaleList[locCount].setFromPOSIXID(uloc_getAvailable(locCount));
73
9.06k
    }
74
10
    ucln_common_registerCleanup(UCLN_COMMON_LOCALE_AVAILABLE, locale_available_cleanup);
75
10
}
76
77
const Locale* U_EXPORT2
78
Locale::getAvailableLocales(int32_t& count)
79
40.8k
{
80
40.8k
    umtx_initOnce(gInitOnceLocale, &locale_available_init);
81
40.8k
    count = availableLocaleListCount;
82
40.8k
    return availableLocaleList;
83
40.8k
}
84
85
86
U_NAMESPACE_END
87
88
// C API ------------------------------------------------------------------- ***
89
90
U_NAMESPACE_USE
91
92
/* ### Constants **************************************************/
93
94
namespace {
95
96
// Enough capacity for the two lists in the res_index.res file
97
const char** gAvailableLocaleNames[2] = {};
98
int32_t gAvailableLocaleCounts[2] = {};
99
icu::UInitOnce ginstalledLocalesInitOnce {};
100
101
class AvailableLocalesSink : public ResourceSink {
102
  public:
103
10
    void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override {
104
10
        if (U_FAILURE(status)) { return; }
105
10
        ResourceTable resIndexTable = value.getTable(status);
106
10
        if (U_FAILURE(status)) { return; }
107
40
        for (int32_t i = 0; resIndexTable.getKeyAndValue(i, key, value); ++i) {
108
30
            ULocAvailableType type;
109
30
            if (uprv_strcmp(key, "InstalledLocales") == 0) {
110
10
                type = ULOC_AVAILABLE_DEFAULT;
111
20
            } else if (uprv_strcmp(key, "AliasLocales") == 0) {
112
10
                type = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES;
113
10
            } else {
114
                // CLDRVersion, etc.
115
10
                continue;
116
10
            }
117
20
            ResourceTable availableLocalesTable = value.getTable(status);
118
20
            if (U_FAILURE(status)) {
119
0
                return;
120
0
            }
121
20
            gAvailableLocaleCounts[type] = availableLocalesTable.getSize();
122
20
            gAvailableLocaleNames[type] = static_cast<const char**>(
123
20
                uprv_malloc(gAvailableLocaleCounts[type] * sizeof(const char*)));
124
20
            if (gAvailableLocaleNames[type] == nullptr) {
125
0
                status = U_MEMORY_ALLOCATION_ERROR;
126
0
                return;
127
0
            }
128
9.61k
            for (int32_t j = 0; availableLocalesTable.getKeyAndValue(j, key, value); ++j) {
129
9.59k
                gAvailableLocaleNames[type][j] = key;
130
9.59k
            }
131
20
        }
132
10
    }
133
};
134
135
class AvailableLocalesStringEnumeration : public StringEnumeration {
136
  public:
137
0
    AvailableLocalesStringEnumeration(ULocAvailableType type) : fType(type) {
138
0
    }
139
140
0
    const char* next(int32_t *resultLength, UErrorCode &status) override {
141
0
        if (U_FAILURE(status)) { return nullptr; }
142
0
        ULocAvailableType actualType = fType;
143
0
        int32_t actualIndex = fIndex++;
144
145
        // If the "combined" list was requested, resolve that now
146
0
        if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) {
147
0
            int32_t defaultLocalesCount = gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT];
148
0
            if (actualIndex < defaultLocalesCount) {
149
0
                actualType = ULOC_AVAILABLE_DEFAULT;
150
0
            } else {
151
0
                actualIndex -= defaultLocalesCount;
152
0
                actualType = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES;
153
0
            }
154
0
        }
155
156
        // Return the requested string
157
0
        int32_t count = gAvailableLocaleCounts[actualType];
158
0
        const char* result;
159
0
        if (actualIndex < count) {
160
0
            result = gAvailableLocaleNames[actualType][actualIndex];
161
0
            if (resultLength != nullptr) {
162
0
                *resultLength = static_cast<int32_t>(uprv_strlen(result));
163
0
            }
164
0
        } else {
165
0
            result = nullptr;
166
0
            if (resultLength != nullptr) {
167
0
                *resultLength = 0;
168
0
            }
169
0
        }
170
0
        return result;
171
0
    }
172
173
0
    void reset(UErrorCode &status) override {
174
0
        if (U_FAILURE(status)) { return; }
175
0
        fIndex = 0;
176
0
    }
177
178
0
    int32_t count(UErrorCode &status) const override {
179
0
        if (U_FAILURE(status)) { return 0; }
180
0
        if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) {
181
0
            return gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT]
182
0
                + gAvailableLocaleCounts[ULOC_AVAILABLE_ONLY_LEGACY_ALIASES];
183
0
        } else {
184
0
            return gAvailableLocaleCounts[fType];
185
0
        }
186
0
    }
187
188
  private:
189
    ULocAvailableType fType;
190
    int32_t fIndex = 0;
191
};
192
193
/* ### Get available **************************************************/
194
195
0
UBool U_CALLCONV uloc_cleanup() {
196
0
    for (int32_t i = 0; i < UPRV_LENGTHOF(gAvailableLocaleNames); i++) {
197
0
        uprv_free(gAvailableLocaleNames[i]);
198
0
        gAvailableLocaleNames[i] = nullptr;
199
0
        gAvailableLocaleCounts[i] = 0;
200
0
    }
201
0
    ginstalledLocalesInitOnce.reset();
202
0
    return true;
203
0
}
204
205
// Load Installed Locales. This function will be called exactly once
206
//   via the initOnce mechanism.
207
208
10
void U_CALLCONV loadInstalledLocales(UErrorCode& status) {
209
10
    ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup);
210
211
10
    icu::LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "res_index", &status));
212
10
    AvailableLocalesSink sink;
213
10
    ures_getAllItemsWithFallback(rb.getAlias(), "", sink, status);
214
10
}
215
216
9.07k
void _load_installedLocales(UErrorCode& status) {
217
9.07k
    umtx_initOnce(ginstalledLocalesInitOnce, &loadInstalledLocales, status);
218
9.07k
}
219
220
} // namespace
221
222
U_CAPI const char* U_EXPORT2
223
9.06k
uloc_getAvailable(int32_t offset) {
224
9.06k
    icu::ErrorCode status;
225
9.06k
    _load_installedLocales(status);
226
9.06k
    if (status.isFailure()) {
227
0
        return nullptr;
228
0
    }
229
9.06k
    if (offset > gAvailableLocaleCounts[0]) {
230
        // *status = U_ILLEGAL_ARGUMENT_ERROR;
231
0
        return nullptr;
232
0
    }
233
9.06k
    return gAvailableLocaleNames[0][offset];
234
9.06k
}
235
236
U_CAPI int32_t  U_EXPORT2
237
10
uloc_countAvailable() {
238
10
    icu::ErrorCode status;
239
10
    _load_installedLocales(status);
240
10
    if (status.isFailure()) {
241
0
        return 0;
242
0
    }
243
10
    return gAvailableLocaleCounts[0];
244
10
}
245
246
U_CAPI UEnumeration* U_EXPORT2
247
0
uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status) {
248
0
    if (U_FAILURE(*status)) {
249
0
        return nullptr;
250
0
    }
251
0
    if (type < 0 || type >= ULOC_AVAILABLE_COUNT) {
252
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
253
0
        return nullptr;
254
0
    }
255
0
    _load_installedLocales(*status);
256
0
    if (U_FAILURE(*status)) {
257
0
        return nullptr;
258
0
    }
259
0
    LocalPointer<AvailableLocalesStringEnumeration> result(
260
0
        new AvailableLocalesStringEnumeration(type), *status);
261
0
    if (U_FAILURE(*status)) {
262
0
        return nullptr;
263
0
    }
264
0
    return uenum_openFromStringEnumeration(result.orphan(), status);
265
0
}