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