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