/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 | } |