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