• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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