Coverage Report

Created: 2025-06-24 06:43

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