/src/icu/source/common/locid.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 | | * Copyright (C) 1997-2016, International Business Machines |
6 | | * Corporation and others. All Rights Reserved. |
7 | | ********************************************************************** |
8 | | * |
9 | | * File locid.cpp |
10 | | * |
11 | | * Created by: Richard Gillam |
12 | | * |
13 | | * Modification History: |
14 | | * |
15 | | * Date Name Description |
16 | | * 02/11/97 aliu Changed gLocPath to fgDataDirectory and added |
17 | | * methods to get and set it. |
18 | | * 04/02/97 aliu Made operator!= inline; fixed return value |
19 | | * of getName(). |
20 | | * 04/15/97 aliu Cleanup for AIX/Win32. |
21 | | * 04/24/97 aliu Numerous changes per code review. |
22 | | * 08/18/98 stephen Changed getDisplayName() |
23 | | * Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE |
24 | | * Added getISOCountries(), getISOLanguages(), |
25 | | * getLanguagesForCountry() |
26 | | * 03/16/99 bertrand rehaul. |
27 | | * 07/21/99 stephen Added U_CFUNC setDefault |
28 | | * 11/09/99 weiv Added const char * getName() const; |
29 | | * 04/12/00 srl removing unicodestring api's and cached hash code |
30 | | * 08/10/01 grhoten Change the static Locales to accessor functions |
31 | | ****************************************************************************** |
32 | | */ |
33 | | |
34 | | |
35 | | #include "unicode/locid.h" |
36 | | #include "unicode/strenum.h" |
37 | | #include "unicode/uloc.h" |
38 | | #include "putilimp.h" |
39 | | #include "mutex.h" |
40 | | #include "umutex.h" |
41 | | #include "uassert.h" |
42 | | #include "cmemory.h" |
43 | | #include "cstring.h" |
44 | | #include "uassert.h" |
45 | | #include "uhash.h" |
46 | | #include "ucln_cmn.h" |
47 | | #include "ustr_imp.h" |
48 | | #include "charstr.h" |
49 | | |
50 | | U_CDECL_BEGIN |
51 | | static UBool U_CALLCONV locale_cleanup(void); |
52 | | U_CDECL_END |
53 | | |
54 | | U_NAMESPACE_BEGIN |
55 | | |
56 | | static Locale *gLocaleCache = NULL; |
57 | | static UInitOnce gLocaleCacheInitOnce = U_INITONCE_INITIALIZER; |
58 | | |
59 | | // gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale. |
60 | | static UMutex gDefaultLocaleMutex = U_MUTEX_INITIALIZER; |
61 | | static UHashtable *gDefaultLocalesHashT = NULL; |
62 | | static Locale *gDefaultLocale = NULL; |
63 | | |
64 | | /** |
65 | | * \def ULOC_STRING_LIMIT |
66 | | * strings beyond this value crash in CharString |
67 | | */ |
68 | 0 | #define ULOC_STRING_LIMIT 357913941 |
69 | | |
70 | | U_NAMESPACE_END |
71 | | |
72 | | typedef enum ELocalePos { |
73 | | eENGLISH, |
74 | | eFRENCH, |
75 | | eGERMAN, |
76 | | eITALIAN, |
77 | | eJAPANESE, |
78 | | eKOREAN, |
79 | | eCHINESE, |
80 | | |
81 | | eFRANCE, |
82 | | eGERMANY, |
83 | | eITALY, |
84 | | eJAPAN, |
85 | | eKOREA, |
86 | | eCHINA, /* Alias for PRC */ |
87 | | eTAIWAN, |
88 | | eUK, |
89 | | eUS, |
90 | | eCANADA, |
91 | | eCANADA_FRENCH, |
92 | | eROOT, |
93 | | |
94 | | |
95 | | //eDEFAULT, |
96 | | eMAX_LOCALES |
97 | | } ELocalePos; |
98 | | |
99 | | U_CFUNC int32_t locale_getKeywords(const char *localeID, |
100 | | char prev, |
101 | | char *keywords, int32_t keywordCapacity, |
102 | | char *values, int32_t valuesCapacity, int32_t *valLen, |
103 | | UBool valuesToo, |
104 | | UErrorCode *status); |
105 | | |
106 | | U_CDECL_BEGIN |
107 | | // |
108 | | // Deleter function for Locales owned by the default Locale hash table/ |
109 | | // |
110 | | static void U_CALLCONV |
111 | 1.21k | deleteLocale(void *obj) { |
112 | 1.21k | delete (icu::Locale *) obj; |
113 | 1.21k | } |
114 | | |
115 | | static UBool U_CALLCONV locale_cleanup(void) |
116 | 1.21k | { |
117 | 1.21k | U_NAMESPACE_USE |
118 | | |
119 | 1.21k | delete [] gLocaleCache; |
120 | 1.21k | gLocaleCache = NULL; |
121 | 1.21k | gLocaleCacheInitOnce.reset(); |
122 | | |
123 | 1.21k | if (gDefaultLocalesHashT) { |
124 | 1.21k | uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func. |
125 | 1.21k | gDefaultLocalesHashT = NULL; |
126 | 1.21k | } |
127 | 1.21k | gDefaultLocale = NULL; |
128 | 1.21k | return TRUE; |
129 | 1.21k | } |
130 | | |
131 | | |
132 | 0 | static void U_CALLCONV locale_init(UErrorCode &status) { |
133 | 0 | U_NAMESPACE_USE |
134 | |
|
135 | 0 | U_ASSERT(gLocaleCache == NULL); |
136 | 0 | gLocaleCache = new Locale[(int)eMAX_LOCALES]; |
137 | 0 | if (gLocaleCache == NULL) { |
138 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
139 | 0 | return; |
140 | 0 | } |
141 | 0 | ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); |
142 | 0 | gLocaleCache[eROOT] = Locale(""); |
143 | 0 | gLocaleCache[eENGLISH] = Locale("en"); |
144 | 0 | gLocaleCache[eFRENCH] = Locale("fr"); |
145 | 0 | gLocaleCache[eGERMAN] = Locale("de"); |
146 | 0 | gLocaleCache[eITALIAN] = Locale("it"); |
147 | 0 | gLocaleCache[eJAPANESE] = Locale("ja"); |
148 | 0 | gLocaleCache[eKOREAN] = Locale("ko"); |
149 | 0 | gLocaleCache[eCHINESE] = Locale("zh"); |
150 | 0 | gLocaleCache[eFRANCE] = Locale("fr", "FR"); |
151 | 0 | gLocaleCache[eGERMANY] = Locale("de", "DE"); |
152 | 0 | gLocaleCache[eITALY] = Locale("it", "IT"); |
153 | 0 | gLocaleCache[eJAPAN] = Locale("ja", "JP"); |
154 | 0 | gLocaleCache[eKOREA] = Locale("ko", "KR"); |
155 | 0 | gLocaleCache[eCHINA] = Locale("zh", "CN"); |
156 | 0 | gLocaleCache[eTAIWAN] = Locale("zh", "TW"); |
157 | 0 | gLocaleCache[eUK] = Locale("en", "GB"); |
158 | 0 | gLocaleCache[eUS] = Locale("en", "US"); |
159 | 0 | gLocaleCache[eCANADA] = Locale("en", "CA"); |
160 | 0 | gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA"); |
161 | 0 | } |
162 | | |
163 | | U_CDECL_END |
164 | | |
165 | | U_NAMESPACE_BEGIN |
166 | | |
167 | 1.21k | Locale *locale_set_default_internal(const char *id, UErrorCode& status) { |
168 | | // Synchronize this entire function. |
169 | 1.21k | Mutex lock(&gDefaultLocaleMutex); |
170 | | |
171 | 1.21k | UBool canonicalize = FALSE; |
172 | | |
173 | | // If given a NULL string for the locale id, grab the default |
174 | | // name from the system. |
175 | | // (Different from most other locale APIs, where a null name means use |
176 | | // the current ICU default locale.) |
177 | 1.21k | if (id == NULL) { |
178 | 1.21k | id = uprv_getDefaultLocaleID(); // This function not thread safe? TODO: verify. |
179 | 1.21k | canonicalize = TRUE; // always canonicalize host ID |
180 | 1.21k | } |
181 | | |
182 | 1.21k | char localeNameBuf[512]; |
183 | | |
184 | 1.21k | if (canonicalize) { |
185 | 1.21k | uloc_canonicalize(id, localeNameBuf, sizeof(localeNameBuf)-1, &status); |
186 | 1.21k | } else { |
187 | 0 | uloc_getName(id, localeNameBuf, sizeof(localeNameBuf)-1, &status); |
188 | 0 | } |
189 | 1.21k | localeNameBuf[sizeof(localeNameBuf)-1] = 0; // Force null termination in event of |
190 | | // a long name filling the buffer. |
191 | | // (long names are truncated.) |
192 | | // |
193 | 1.21k | if (U_FAILURE(status)) { |
194 | 0 | return gDefaultLocale; |
195 | 0 | } |
196 | | |
197 | 1.21k | if (gDefaultLocalesHashT == NULL) { |
198 | 1.21k | gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); |
199 | 1.21k | if (U_FAILURE(status)) { |
200 | 0 | return gDefaultLocale; |
201 | 0 | } |
202 | 1.21k | uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale); |
203 | 1.21k | ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); |
204 | 1.21k | } |
205 | | |
206 | 1.21k | Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf); |
207 | 1.21k | if (newDefault == NULL) { |
208 | 1.21k | newDefault = new Locale(Locale::eBOGUS); |
209 | 1.21k | if (newDefault == NULL) { |
210 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
211 | 0 | return gDefaultLocale; |
212 | 0 | } |
213 | 1.21k | newDefault->init(localeNameBuf, FALSE); |
214 | 1.21k | uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status); |
215 | 1.21k | if (U_FAILURE(status)) { |
216 | 0 | return gDefaultLocale; |
217 | 0 | } |
218 | 1.21k | } |
219 | 1.21k | gDefaultLocale = newDefault; |
220 | 1.21k | return gDefaultLocale; |
221 | 1.21k | } |
222 | | |
223 | | U_NAMESPACE_END |
224 | | |
225 | | /* sfb 07/21/99 */ |
226 | | U_CFUNC void |
227 | | locale_set_default(const char *id) |
228 | 0 | { |
229 | 0 | U_NAMESPACE_USE |
230 | 0 | UErrorCode status = U_ZERO_ERROR; |
231 | 0 | locale_set_default_internal(id, status); |
232 | 0 | } |
233 | | /* end */ |
234 | | |
235 | | U_CFUNC const char * |
236 | | locale_get_default(void) |
237 | 3.65k | { |
238 | 3.65k | U_NAMESPACE_USE |
239 | 3.65k | return Locale::getDefault().getName(); |
240 | 3.65k | } |
241 | | |
242 | | |
243 | | U_NAMESPACE_BEGIN |
244 | | |
245 | | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale) |
246 | | |
247 | | /*Character separating the posix id fields*/ |
248 | | // '_' |
249 | | // In the platform codepage. |
250 | 0 | #define SEP_CHAR '_' |
251 | | |
252 | | Locale::~Locale() |
253 | 1.21k | { |
254 | 1.21k | if (baseName != fullName) { |
255 | 0 | uprv_free(baseName); |
256 | 0 | } |
257 | 1.21k | baseName = NULL; |
258 | | /*if fullName is on the heap, we free it*/ |
259 | 1.21k | if (fullName != fullNameBuffer) |
260 | 0 | { |
261 | 0 | uprv_free(fullName); |
262 | 0 | fullName = NULL; |
263 | 0 | } |
264 | 1.21k | } |
265 | | |
266 | | Locale::Locale() |
267 | 0 | : UObject(), fullName(fullNameBuffer), baseName(NULL) |
268 | 0 | { |
269 | 0 | init(NULL, FALSE); |
270 | 0 | } |
271 | | |
272 | | /* |
273 | | * Internal constructor to allow construction of a locale object with |
274 | | * NO side effects. (Default constructor tries to get |
275 | | * the default locale.) |
276 | | */ |
277 | | Locale::Locale(Locale::ELocaleType) |
278 | 1.21k | : UObject(), fullName(fullNameBuffer), baseName(NULL) |
279 | 1.21k | { |
280 | 1.21k | setToBogus(); |
281 | 1.21k | } |
282 | | |
283 | | |
284 | | Locale::Locale( const char * newLanguage, |
285 | | const char * newCountry, |
286 | | const char * newVariant, |
287 | | const char * newKeywords) |
288 | 0 | : UObject(), fullName(fullNameBuffer), baseName(NULL) |
289 | 0 | { |
290 | 0 | if( (newLanguage==NULL) && (newCountry == NULL) && (newVariant == NULL) ) |
291 | 0 | { |
292 | 0 | init(NULL, FALSE); /* shortcut */ |
293 | 0 | } |
294 | 0 | else |
295 | 0 | { |
296 | 0 | UErrorCode status = U_ZERO_ERROR; |
297 | 0 | int32_t size = 0; |
298 | 0 | int32_t lsize = 0; |
299 | 0 | int32_t csize = 0; |
300 | 0 | int32_t vsize = 0; |
301 | 0 | int32_t ksize = 0; |
302 | | |
303 | | // Calculate the size of the resulting string. |
304 | | |
305 | | // Language |
306 | 0 | if ( newLanguage != NULL ) |
307 | 0 | { |
308 | 0 | lsize = (int32_t)uprv_strlen(newLanguage); |
309 | 0 | if ( lsize < 0 || lsize > ULOC_STRING_LIMIT ) { // int32 wrap |
310 | 0 | setToBogus(); |
311 | 0 | return; |
312 | 0 | } |
313 | 0 | size = lsize; |
314 | 0 | } |
315 | | |
316 | 0 | CharString togo(newLanguage, lsize, status); // start with newLanguage |
317 | | |
318 | | // _Country |
319 | 0 | if ( newCountry != NULL ) |
320 | 0 | { |
321 | 0 | csize = (int32_t)uprv_strlen(newCountry); |
322 | 0 | if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap |
323 | 0 | setToBogus(); |
324 | 0 | return; |
325 | 0 | } |
326 | 0 | size += csize; |
327 | 0 | } |
328 | | |
329 | | // _Variant |
330 | 0 | if ( newVariant != NULL ) |
331 | 0 | { |
332 | | // remove leading _'s |
333 | 0 | while(newVariant[0] == SEP_CHAR) |
334 | 0 | { |
335 | 0 | newVariant++; |
336 | 0 | } |
337 | | |
338 | | // remove trailing _'s |
339 | 0 | vsize = (int32_t)uprv_strlen(newVariant); |
340 | 0 | if ( vsize < 0 || vsize > ULOC_STRING_LIMIT ) { // int32 wrap |
341 | 0 | setToBogus(); |
342 | 0 | return; |
343 | 0 | } |
344 | 0 | while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) ) |
345 | 0 | { |
346 | 0 | vsize--; |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | 0 | if( vsize > 0 ) |
351 | 0 | { |
352 | 0 | size += vsize; |
353 | 0 | } |
354 | | |
355 | | // Separator rules: |
356 | 0 | if ( vsize > 0 ) |
357 | 0 | { |
358 | 0 | size += 2; // at least: __v |
359 | 0 | } |
360 | 0 | else if ( csize > 0 ) |
361 | 0 | { |
362 | 0 | size += 1; // at least: _v |
363 | 0 | } |
364 | |
|
365 | 0 | if ( newKeywords != NULL) |
366 | 0 | { |
367 | 0 | ksize = (int32_t)uprv_strlen(newKeywords); |
368 | 0 | if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) { |
369 | 0 | setToBogus(); |
370 | 0 | return; |
371 | 0 | } |
372 | 0 | size += ksize + 1; |
373 | 0 | } |
374 | | |
375 | | // NOW we have the full locale string.. |
376 | | // Now, copy it back. |
377 | | |
378 | | // newLanguage is already copied |
379 | | |
380 | 0 | if ( ( vsize != 0 ) || (csize != 0) ) // at least: __v |
381 | 0 | { // ^ |
382 | 0 | togo.append(SEP_CHAR, status); |
383 | 0 | } |
384 | |
|
385 | 0 | if ( csize != 0 ) |
386 | 0 | { |
387 | 0 | togo.append(newCountry, status); |
388 | 0 | } |
389 | |
|
390 | 0 | if ( vsize != 0) |
391 | 0 | { |
392 | 0 | togo.append(SEP_CHAR, status) |
393 | 0 | .append(newVariant, vsize, status); |
394 | 0 | } |
395 | |
|
396 | 0 | if ( ksize != 0) |
397 | 0 | { |
398 | 0 | if (uprv_strchr(newKeywords, '=')) { |
399 | 0 | togo.append('@', status); /* keyword parsing */ |
400 | 0 | } |
401 | 0 | else { |
402 | 0 | togo.append('_', status); /* Variant parsing with a script */ |
403 | 0 | if ( vsize == 0) { |
404 | 0 | togo.append('_', status); /* No country found */ |
405 | 0 | } |
406 | 0 | } |
407 | 0 | togo.append(newKeywords, status); |
408 | 0 | } |
409 | |
|
410 | 0 | if (U_FAILURE(status)) { |
411 | | // Something went wrong with appending, etc. |
412 | 0 | setToBogus(); |
413 | 0 | return; |
414 | 0 | } |
415 | | // Parse it, because for example 'language' might really be a complete |
416 | | // string. |
417 | 0 | init(togo.data(), FALSE); |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | | Locale::Locale(const Locale &other) |
422 | 0 | : UObject(other), fullName(fullNameBuffer), baseName(NULL) |
423 | 0 | { |
424 | 0 | *this = other; |
425 | 0 | } |
426 | | |
427 | | Locale &Locale::operator=(const Locale &other) |
428 | 0 | { |
429 | 0 | if (this == &other) { |
430 | 0 | return *this; |
431 | 0 | } |
432 | | |
433 | | /* Free our current storage */ |
434 | 0 | if (baseName != fullName) { |
435 | 0 | uprv_free(baseName); |
436 | 0 | } |
437 | 0 | baseName = NULL; |
438 | 0 | if(fullName != fullNameBuffer) { |
439 | 0 | uprv_free(fullName); |
440 | 0 | fullName = fullNameBuffer; |
441 | 0 | } |
442 | | |
443 | | /* Allocate the full name if necessary */ |
444 | 0 | if(other.fullName != other.fullNameBuffer) { |
445 | 0 | fullName = (char *)uprv_malloc(sizeof(char)*(uprv_strlen(other.fullName)+1)); |
446 | 0 | if (fullName == NULL) { |
447 | 0 | return *this; |
448 | 0 | } |
449 | 0 | } |
450 | | /* Copy the full name */ |
451 | 0 | uprv_strcpy(fullName, other.fullName); |
452 | | |
453 | | /* Copy the baseName if it differs from fullName. */ |
454 | 0 | if (other.baseName == other.fullName) { |
455 | 0 | baseName = fullName; |
456 | 0 | } else { |
457 | 0 | if (other.baseName) { |
458 | 0 | baseName = uprv_strdup(other.baseName); |
459 | 0 | } |
460 | 0 | } |
461 | | |
462 | | /* Copy the language and country fields */ |
463 | 0 | uprv_strcpy(language, other.language); |
464 | 0 | uprv_strcpy(script, other.script); |
465 | 0 | uprv_strcpy(country, other.country); |
466 | | |
467 | | /* The variantBegin is an offset, just copy it */ |
468 | 0 | variantBegin = other.variantBegin; |
469 | 0 | fIsBogus = other.fIsBogus; |
470 | 0 | return *this; |
471 | 0 | } |
472 | | |
473 | | Locale * |
474 | 0 | Locale::clone() const { |
475 | 0 | return new Locale(*this); |
476 | 0 | } |
477 | | |
478 | | UBool |
479 | | Locale::operator==( const Locale& other) const |
480 | 0 | { |
481 | 0 | return (uprv_strcmp(other.fullName, fullName) == 0); |
482 | 0 | } |
483 | | |
484 | 3.65k | #define ISASCIIALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) |
485 | | |
486 | | /*This function initializes a Locale from a C locale ID*/ |
487 | | Locale& Locale::init(const char* localeID, UBool canonicalize) |
488 | 1.21k | { |
489 | 1.21k | fIsBogus = FALSE; |
490 | | /* Free our current storage */ |
491 | 1.21k | if (baseName != fullName) { |
492 | 1.21k | uprv_free(baseName); |
493 | 1.21k | } |
494 | 1.21k | baseName = NULL; |
495 | 1.21k | if(fullName != fullNameBuffer) { |
496 | 0 | uprv_free(fullName); |
497 | 0 | fullName = fullNameBuffer; |
498 | 0 | } |
499 | | |
500 | | // not a loop: |
501 | | // just an easy way to have a common error-exit |
502 | | // without goto and without another function |
503 | 1.21k | do { |
504 | 1.21k | char *separator; |
505 | 1.21k | char *field[5] = {0}; |
506 | 1.21k | int32_t fieldLen[5] = {0}; |
507 | 1.21k | int32_t fieldIdx; |
508 | 1.21k | int32_t variantField; |
509 | 1.21k | int32_t length; |
510 | 1.21k | UErrorCode err; |
511 | | |
512 | 1.21k | if(localeID == NULL) { |
513 | | // not an error, just set the default locale |
514 | 0 | return *this = getDefault(); |
515 | 0 | } |
516 | | |
517 | | /* preset all fields to empty */ |
518 | 1.21k | language[0] = script[0] = country[0] = 0; |
519 | | |
520 | | // "canonicalize" the locale ID to ICU/Java format |
521 | 1.21k | err = U_ZERO_ERROR; |
522 | 1.21k | length = canonicalize ? |
523 | 0 | uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) : |
524 | 1.21k | uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err); |
525 | | |
526 | 1.21k | if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) { |
527 | | /*Go to heap for the fullName if necessary*/ |
528 | 0 | fullName = (char *)uprv_malloc(sizeof(char)*(length + 1)); |
529 | 0 | if(fullName == 0) { |
530 | 0 | fullName = fullNameBuffer; |
531 | 0 | break; // error: out of memory |
532 | 0 | } |
533 | 0 | err = U_ZERO_ERROR; |
534 | 0 | length = canonicalize ? |
535 | 0 | uloc_canonicalize(localeID, fullName, length+1, &err) : |
536 | 0 | uloc_getName(localeID, fullName, length+1, &err); |
537 | 0 | } |
538 | 1.21k | if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { |
539 | | /* should never occur */ |
540 | 0 | break; |
541 | 0 | } |
542 | | |
543 | 1.21k | variantBegin = length; |
544 | | |
545 | | /* after uloc_getName/canonicalize() we know that only '_' are separators */ |
546 | 1.21k | separator = field[0] = fullName; |
547 | 1.21k | fieldIdx = 1; |
548 | 3.65k | while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) && fieldIdx < UPRV_LENGTHOF(field)-1) { |
549 | 2.43k | field[fieldIdx] = separator + 1; |
550 | 2.43k | fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); |
551 | 2.43k | fieldIdx++; |
552 | 2.43k | } |
553 | | // variant may contain @foo or .foo POSIX cruft; remove it |
554 | 1.21k | separator = uprv_strchr(field[fieldIdx-1], '@'); |
555 | 1.21k | char* sep2 = uprv_strchr(field[fieldIdx-1], '.'); |
556 | 1.21k | if (separator!=NULL || sep2!=NULL) { |
557 | 0 | if (separator==NULL || (sep2!=NULL && separator > sep2)) { |
558 | 0 | separator = sep2; |
559 | 0 | } |
560 | 0 | fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); |
561 | 1.21k | } else { |
562 | 1.21k | fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName); |
563 | 1.21k | } |
564 | | |
565 | 1.21k | if (fieldLen[0] >= (int32_t)(sizeof(language))) |
566 | 0 | { |
567 | 0 | break; // error: the language field is too long |
568 | 0 | } |
569 | | |
570 | 1.21k | variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */ |
571 | 1.21k | if (fieldLen[0] > 0) { |
572 | | /* We have a language */ |
573 | 1.21k | uprv_memcpy(language, fullName, fieldLen[0]); |
574 | 1.21k | language[fieldLen[0]] = 0; |
575 | 1.21k | } |
576 | 1.21k | if (fieldLen[1] == 4 && ISASCIIALPHA(field[1][0]) && |
577 | 1.21k | ISASCIIALPHA(field[1][1]) && ISASCIIALPHA(field[1][2]) && |
578 | 1.21k | ISASCIIALPHA(field[1][3])) { |
579 | | /* We have at least a script */ |
580 | 0 | uprv_memcpy(script, field[1], fieldLen[1]); |
581 | 0 | script[fieldLen[1]] = 0; |
582 | 0 | variantField++; |
583 | 0 | } |
584 | | |
585 | 1.21k | if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) { |
586 | | /* We have a country */ |
587 | 1.21k | uprv_memcpy(country, field[variantField], fieldLen[variantField]); |
588 | 1.21k | country[fieldLen[variantField]] = 0; |
589 | 1.21k | variantField++; |
590 | 1.21k | } else if (fieldLen[variantField] == 0) { |
591 | 0 | variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */ |
592 | 0 | } |
593 | | |
594 | 1.21k | if (fieldLen[variantField] > 0) { |
595 | | /* We have a variant */ |
596 | 1.21k | variantBegin = (int32_t)(field[variantField] - fullName); |
597 | 1.21k | } |
598 | | |
599 | 1.21k | err = U_ZERO_ERROR; |
600 | 1.21k | initBaseName(err); |
601 | 1.21k | if (U_FAILURE(err)) { |
602 | 0 | break; |
603 | 0 | } |
604 | | |
605 | | // successful end of init() |
606 | 1.21k | return *this; |
607 | 1.21k | } while(0); /*loop doesn't iterate*/ |
608 | | |
609 | | // when an error occurs, then set this object to "bogus" (there is no UErrorCode here) |
610 | 0 | setToBogus(); |
611 | |
|
612 | 0 | return *this; |
613 | 1.21k | } |
614 | | |
615 | | /* |
616 | | * Set up the base name. |
617 | | * If there are no key words, it's exactly the full name. |
618 | | * If key words exist, it's the full name truncated at the '@' character. |
619 | | * Need to set up both at init() and after setting a keyword. |
620 | | */ |
621 | | void |
622 | 1.21k | Locale::initBaseName(UErrorCode &status) { |
623 | 1.21k | if (U_FAILURE(status)) { |
624 | 0 | return; |
625 | 0 | } |
626 | 1.21k | U_ASSERT(baseName==NULL || baseName==fullName); |
627 | 1.21k | const char *atPtr = uprv_strchr(fullName, '@'); |
628 | 1.21k | const char *eqPtr = uprv_strchr(fullName, '='); |
629 | 1.21k | if (atPtr && eqPtr && atPtr < eqPtr) { |
630 | | // Key words exist. |
631 | 0 | int32_t baseNameLength = (int32_t)(atPtr - fullName); |
632 | 0 | baseName = (char *)uprv_malloc(baseNameLength + 1); |
633 | 0 | if (baseName == NULL) { |
634 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
635 | 0 | return; |
636 | 0 | } |
637 | 0 | uprv_strncpy(baseName, fullName, baseNameLength); |
638 | 0 | baseName[baseNameLength] = 0; |
639 | | |
640 | | // The original computation of variantBegin leaves it equal to the length |
641 | | // of fullName if there is no variant. It should instead be |
642 | | // the length of the baseName. |
643 | 0 | if (variantBegin > baseNameLength) { |
644 | 0 | variantBegin = baseNameLength; |
645 | 0 | } |
646 | 1.21k | } else { |
647 | 1.21k | baseName = fullName; |
648 | 1.21k | } |
649 | 1.21k | } |
650 | | |
651 | | |
652 | | int32_t |
653 | | Locale::hashCode() const |
654 | 0 | { |
655 | 0 | return ustr_hashCharsN(fullName, uprv_strlen(fullName)); |
656 | 0 | } |
657 | | |
658 | | void |
659 | 1.21k | Locale::setToBogus() { |
660 | | /* Free our current storage */ |
661 | 1.21k | if(baseName != fullName) { |
662 | 1.21k | uprv_free(baseName); |
663 | 1.21k | } |
664 | 1.21k | baseName = NULL; |
665 | 1.21k | if(fullName != fullNameBuffer) { |
666 | 0 | uprv_free(fullName); |
667 | 0 | fullName = fullNameBuffer; |
668 | 0 | } |
669 | 1.21k | *fullNameBuffer = 0; |
670 | 1.21k | *language = 0; |
671 | 1.21k | *script = 0; |
672 | 1.21k | *country = 0; |
673 | 1.21k | fIsBogus = TRUE; |
674 | 1.21k | variantBegin = 0; |
675 | 1.21k | } |
676 | | |
677 | | const Locale& U_EXPORT2 |
678 | | Locale::getDefault() |
679 | 3.65k | { |
680 | 3.65k | { |
681 | 3.65k | Mutex lock(&gDefaultLocaleMutex); |
682 | 3.65k | if (gDefaultLocale != NULL) { |
683 | 2.43k | return *gDefaultLocale; |
684 | 2.43k | } |
685 | 3.65k | } |
686 | 1.21k | UErrorCode status = U_ZERO_ERROR; |
687 | 1.21k | return *locale_set_default_internal(NULL, status); |
688 | 3.65k | } |
689 | | |
690 | | |
691 | | |
692 | | void U_EXPORT2 |
693 | | Locale::setDefault( const Locale& newLocale, |
694 | | UErrorCode& status) |
695 | 0 | { |
696 | 0 | if (U_FAILURE(status)) { |
697 | 0 | return; |
698 | 0 | } |
699 | | |
700 | | /* Set the default from the full name string of the supplied locale. |
701 | | * This is a convenient way to access the default locale caching mechanisms. |
702 | | */ |
703 | 0 | const char *localeID = newLocale.getName(); |
704 | 0 | locale_set_default_internal(localeID, status); |
705 | 0 | } |
706 | | |
707 | | Locale U_EXPORT2 |
708 | | Locale::createFromName (const char *name) |
709 | 0 | { |
710 | 0 | if (name) { |
711 | 0 | Locale l(""); |
712 | 0 | l.init(name, FALSE); |
713 | 0 | return l; |
714 | 0 | } |
715 | 0 | else { |
716 | 0 | return getDefault(); |
717 | 0 | } |
718 | 0 | } |
719 | | |
720 | | Locale U_EXPORT2 |
721 | 0 | Locale::createCanonical(const char* name) { |
722 | 0 | Locale loc(""); |
723 | 0 | loc.init(name, TRUE); |
724 | 0 | return loc; |
725 | 0 | } |
726 | | |
727 | | const char * |
728 | | Locale::getISO3Language() const |
729 | 0 | { |
730 | 0 | return uloc_getISO3Language(fullName); |
731 | 0 | } |
732 | | |
733 | | |
734 | | const char * |
735 | | Locale::getISO3Country() const |
736 | 0 | { |
737 | 0 | return uloc_getISO3Country(fullName); |
738 | 0 | } |
739 | | |
740 | | /** |
741 | | * Return the LCID value as specified in the "LocaleID" resource for this |
742 | | * locale. The LocaleID must be expressed as a hexadecimal number, from |
743 | | * one to four digits. If the LocaleID resource is not present, or is |
744 | | * in an incorrect format, 0 is returned. The LocaleID is for use in |
745 | | * Windows (it is an LCID), but is available on all platforms. |
746 | | */ |
747 | | uint32_t |
748 | | Locale::getLCID() const |
749 | 0 | { |
750 | 0 | return uloc_getLCID(fullName); |
751 | 0 | } |
752 | | |
753 | | const char* const* U_EXPORT2 Locale::getISOCountries() |
754 | 0 | { |
755 | 0 | return uloc_getISOCountries(); |
756 | 0 | } |
757 | | |
758 | | const char* const* U_EXPORT2 Locale::getISOLanguages() |
759 | 0 | { |
760 | 0 | return uloc_getISOLanguages(); |
761 | 0 | } |
762 | | |
763 | | // Set the locale's data based on a posix id. |
764 | | void Locale::setFromPOSIXID(const char *posixID) |
765 | 0 | { |
766 | 0 | init(posixID, TRUE); |
767 | 0 | } |
768 | | |
769 | | const Locale & U_EXPORT2 |
770 | | Locale::getRoot(void) |
771 | 0 | { |
772 | 0 | return getLocale(eROOT); |
773 | 0 | } |
774 | | |
775 | | const Locale & U_EXPORT2 |
776 | | Locale::getEnglish(void) |
777 | 0 | { |
778 | 0 | return getLocale(eENGLISH); |
779 | 0 | } |
780 | | |
781 | | const Locale & U_EXPORT2 |
782 | | Locale::getFrench(void) |
783 | 0 | { |
784 | 0 | return getLocale(eFRENCH); |
785 | 0 | } |
786 | | |
787 | | const Locale & U_EXPORT2 |
788 | | Locale::getGerman(void) |
789 | 0 | { |
790 | 0 | return getLocale(eGERMAN); |
791 | 0 | } |
792 | | |
793 | | const Locale & U_EXPORT2 |
794 | | Locale::getItalian(void) |
795 | 0 | { |
796 | 0 | return getLocale(eITALIAN); |
797 | 0 | } |
798 | | |
799 | | const Locale & U_EXPORT2 |
800 | | Locale::getJapanese(void) |
801 | 0 | { |
802 | 0 | return getLocale(eJAPANESE); |
803 | 0 | } |
804 | | |
805 | | const Locale & U_EXPORT2 |
806 | | Locale::getKorean(void) |
807 | 0 | { |
808 | 0 | return getLocale(eKOREAN); |
809 | 0 | } |
810 | | |
811 | | const Locale & U_EXPORT2 |
812 | | Locale::getChinese(void) |
813 | 0 | { |
814 | 0 | return getLocale(eCHINESE); |
815 | 0 | } |
816 | | |
817 | | const Locale & U_EXPORT2 |
818 | | Locale::getSimplifiedChinese(void) |
819 | 0 | { |
820 | 0 | return getLocale(eCHINA); |
821 | 0 | } |
822 | | |
823 | | const Locale & U_EXPORT2 |
824 | | Locale::getTraditionalChinese(void) |
825 | 0 | { |
826 | 0 | return getLocale(eTAIWAN); |
827 | 0 | } |
828 | | |
829 | | |
830 | | const Locale & U_EXPORT2 |
831 | | Locale::getFrance(void) |
832 | 0 | { |
833 | 0 | return getLocale(eFRANCE); |
834 | 0 | } |
835 | | |
836 | | const Locale & U_EXPORT2 |
837 | | Locale::getGermany(void) |
838 | 0 | { |
839 | 0 | return getLocale(eGERMANY); |
840 | 0 | } |
841 | | |
842 | | const Locale & U_EXPORT2 |
843 | | Locale::getItaly(void) |
844 | 0 | { |
845 | 0 | return getLocale(eITALY); |
846 | 0 | } |
847 | | |
848 | | const Locale & U_EXPORT2 |
849 | | Locale::getJapan(void) |
850 | 0 | { |
851 | 0 | return getLocale(eJAPAN); |
852 | 0 | } |
853 | | |
854 | | const Locale & U_EXPORT2 |
855 | | Locale::getKorea(void) |
856 | 0 | { |
857 | 0 | return getLocale(eKOREA); |
858 | 0 | } |
859 | | |
860 | | const Locale & U_EXPORT2 |
861 | | Locale::getChina(void) |
862 | 0 | { |
863 | 0 | return getLocale(eCHINA); |
864 | 0 | } |
865 | | |
866 | | const Locale & U_EXPORT2 |
867 | | Locale::getPRC(void) |
868 | 0 | { |
869 | 0 | return getLocale(eCHINA); |
870 | 0 | } |
871 | | |
872 | | const Locale & U_EXPORT2 |
873 | | Locale::getTaiwan(void) |
874 | 0 | { |
875 | 0 | return getLocale(eTAIWAN); |
876 | 0 | } |
877 | | |
878 | | const Locale & U_EXPORT2 |
879 | | Locale::getUK(void) |
880 | 0 | { |
881 | 0 | return getLocale(eUK); |
882 | 0 | } |
883 | | |
884 | | const Locale & U_EXPORT2 |
885 | | Locale::getUS(void) |
886 | 0 | { |
887 | 0 | return getLocale(eUS); |
888 | 0 | } |
889 | | |
890 | | const Locale & U_EXPORT2 |
891 | | Locale::getCanada(void) |
892 | 0 | { |
893 | 0 | return getLocale(eCANADA); |
894 | 0 | } |
895 | | |
896 | | const Locale & U_EXPORT2 |
897 | | Locale::getCanadaFrench(void) |
898 | 0 | { |
899 | 0 | return getLocale(eCANADA_FRENCH); |
900 | 0 | } |
901 | | |
902 | | const Locale & |
903 | | Locale::getLocale(int locid) |
904 | 0 | { |
905 | 0 | Locale *localeCache = getLocaleCache(); |
906 | 0 | U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0)); |
907 | 0 | if (localeCache == NULL) { |
908 | | // Failure allocating the locale cache. |
909 | | // The best we can do is return a NULL reference. |
910 | 0 | locid = 0; |
911 | 0 | } |
912 | 0 | return localeCache[locid]; /*operating on NULL*/ |
913 | 0 | } |
914 | | |
915 | | /* |
916 | | This function is defined this way in order to get around static |
917 | | initialization and static destruction. |
918 | | */ |
919 | | Locale * |
920 | | Locale::getLocaleCache(void) |
921 | 0 | { |
922 | 0 | UErrorCode status = U_ZERO_ERROR; |
923 | 0 | umtx_initOnce(gLocaleCacheInitOnce, locale_init, status); |
924 | 0 | return gLocaleCache; |
925 | 0 | } |
926 | | |
927 | | class KeywordEnumeration : public StringEnumeration { |
928 | | private: |
929 | | char *keywords; |
930 | | char *current; |
931 | | int32_t length; |
932 | | UnicodeString currUSKey; |
933 | | static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */ |
934 | | |
935 | | public: |
936 | 0 | static UClassID U_EXPORT2 getStaticClassID(void) { return (UClassID)&fgClassID; } |
937 | 0 | virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); } |
938 | | public: |
939 | | KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status) |
940 | 0 | : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) { |
941 | 0 | if(U_SUCCESS(status) && keywordLen != 0) { |
942 | 0 | if(keys == NULL || keywordLen < 0) { |
943 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
944 | 0 | } else { |
945 | 0 | keywords = (char *)uprv_malloc(keywordLen+1); |
946 | 0 | if (keywords == NULL) { |
947 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
948 | 0 | } |
949 | 0 | else { |
950 | 0 | uprv_memcpy(keywords, keys, keywordLen); |
951 | 0 | keywords[keywordLen] = 0; |
952 | 0 | current = keywords + currentIndex; |
953 | 0 | length = keywordLen; |
954 | 0 | } |
955 | 0 | } |
956 | 0 | } |
957 | 0 | } |
958 | | |
959 | | virtual ~KeywordEnumeration(); |
960 | | |
961 | | virtual StringEnumeration * clone() const |
962 | 0 | { |
963 | 0 | UErrorCode status = U_ZERO_ERROR; |
964 | 0 | return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status); |
965 | 0 | } |
966 | | |
967 | 0 | virtual int32_t count(UErrorCode &/*status*/) const { |
968 | 0 | char *kw = keywords; |
969 | 0 | int32_t result = 0; |
970 | 0 | while(*kw) { |
971 | 0 | result++; |
972 | 0 | kw += uprv_strlen(kw)+1; |
973 | 0 | } |
974 | 0 | return result; |
975 | 0 | } |
976 | | |
977 | 0 | virtual const char* next(int32_t* resultLength, UErrorCode& status) { |
978 | 0 | const char* result; |
979 | 0 | int32_t len; |
980 | 0 | if(U_SUCCESS(status) && *current != 0) { |
981 | 0 | result = current; |
982 | 0 | len = (int32_t)uprv_strlen(current); |
983 | 0 | current += len+1; |
984 | 0 | if(resultLength != NULL) { |
985 | 0 | *resultLength = len; |
986 | 0 | } |
987 | 0 | } else { |
988 | 0 | if(resultLength != NULL) { |
989 | 0 | *resultLength = 0; |
990 | 0 | } |
991 | 0 | result = NULL; |
992 | 0 | } |
993 | 0 | return result; |
994 | 0 | } |
995 | | |
996 | 0 | virtual const UnicodeString* snext(UErrorCode& status) { |
997 | 0 | int32_t resultLength = 0; |
998 | 0 | const char *s = next(&resultLength, status); |
999 | 0 | return setChars(s, resultLength, status); |
1000 | 0 | } |
1001 | | |
1002 | 0 | virtual void reset(UErrorCode& /*status*/) { |
1003 | 0 | current = keywords; |
1004 | 0 | } |
1005 | | }; |
1006 | | |
1007 | | const char KeywordEnumeration::fgClassID = '\0'; |
1008 | | |
1009 | 0 | KeywordEnumeration::~KeywordEnumeration() { |
1010 | 0 | uprv_free(keywords); |
1011 | 0 | } |
1012 | | |
1013 | | StringEnumeration * |
1014 | | Locale::createKeywords(UErrorCode &status) const |
1015 | 0 | { |
1016 | 0 | char keywords[256]; |
1017 | 0 | int32_t keywordCapacity = 256; |
1018 | 0 | StringEnumeration *result = NULL; |
1019 | |
|
1020 | 0 | const char* variantStart = uprv_strchr(fullName, '@'); |
1021 | 0 | const char* assignment = uprv_strchr(fullName, '='); |
1022 | 0 | if(variantStart) { |
1023 | 0 | if(assignment > variantStart) { |
1024 | 0 | int32_t keyLen = locale_getKeywords(variantStart+1, '@', keywords, keywordCapacity, NULL, 0, NULL, FALSE, &status); |
1025 | 0 | if(keyLen) { |
1026 | 0 | result = new KeywordEnumeration(keywords, keyLen, 0, status); |
1027 | 0 | } |
1028 | 0 | } else { |
1029 | 0 | status = U_INVALID_FORMAT_ERROR; |
1030 | 0 | } |
1031 | 0 | } |
1032 | 0 | return result; |
1033 | 0 | } |
1034 | | |
1035 | | int32_t |
1036 | | Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const |
1037 | 0 | { |
1038 | 0 | return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status); |
1039 | 0 | } |
1040 | | |
1041 | | void |
1042 | | Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status) |
1043 | 0 | { |
1044 | 0 | uloc_setKeywordValue(keywordName, keywordValue, fullName, ULOC_FULLNAME_CAPACITY, &status); |
1045 | 0 | if (U_SUCCESS(status) && baseName == fullName) { |
1046 | | // May have added the first keyword, meaning that the fullName is no longer also the baseName. |
1047 | 0 | initBaseName(status); |
1048 | 0 | } |
1049 | 0 | } |
1050 | | |
1051 | | const char * |
1052 | 0 | Locale::getBaseName() const { |
1053 | 0 | return baseName; |
1054 | 0 | } |
1055 | | |
1056 | | //eof |
1057 | | U_NAMESPACE_END |