| |
1 /* |
| |
2 ******************************************************************************* |
| |
3 * |
| |
4 * Copyright (C) 1997-2013, International Business Machines |
| |
5 * Corporation and others. All Rights Reserved. |
| |
6 * |
| |
7 ******************************************************************************* |
| |
8 * file name: locdispnames.cpp |
| |
9 * encoding: US-ASCII |
| |
10 * tab size: 8 (not used) |
| |
11 * indentation:4 |
| |
12 * |
| |
13 * created on: 2010feb25 |
| |
14 * created by: Markus W. Scherer |
| |
15 * |
| |
16 * Code for locale display names, separated out from other .cpp files |
| |
17 * that then do not depend on resource bundle code and display name data. |
| |
18 */ |
| |
19 |
| |
20 #include "unicode/utypes.h" |
| |
21 #include "unicode/brkiter.h" |
| |
22 #include "unicode/locid.h" |
| |
23 #include "unicode/uloc.h" |
| |
24 #include "unicode/ures.h" |
| |
25 #include "unicode/ustring.h" |
| |
26 #include "cmemory.h" |
| |
27 #include "cstring.h" |
| |
28 #include "putilimp.h" |
| |
29 #include "ulocimp.h" |
| |
30 #include "uresimp.h" |
| |
31 #include "ureslocs.h" |
| |
32 #include "ustr_imp.h" |
| |
33 |
| |
34 // C++ API ----------------------------------------------------------------- *** |
| |
35 |
| |
36 U_NAMESPACE_BEGIN |
| |
37 |
| |
38 UnicodeString& |
| |
39 Locale::getDisplayLanguage(UnicodeString& dispLang) const |
| |
40 { |
| |
41 return this->getDisplayLanguage(getDefault(), dispLang); |
| |
42 } |
| |
43 |
| |
44 /*We cannot make any assumptions on the size of the output display strings |
| |
45 * Yet, since we are calling through to a C API, we need to set limits on |
| |
46 * buffer size. For all the following getDisplay functions we first attempt |
| |
47 * to fill up a stack allocated buffer. If it is to small we heap allocated |
| |
48 * the exact buffer we need copy it to the UnicodeString and delete it*/ |
| |
49 |
| |
50 UnicodeString& |
| |
51 Locale::getDisplayLanguage(const Locale &displayLocale, |
| |
52 UnicodeString &result) const { |
| |
53 UChar *buffer; |
| |
54 UErrorCode errorCode=U_ZERO_ERROR; |
| |
55 int32_t length; |
| |
56 |
| |
57 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); |
| |
58 if(buffer==0) { |
| |
59 result.truncate(0); |
| |
60 return result; |
| |
61 } |
| |
62 |
| |
63 length=uloc_getDisplayLanguage(fullName, displayLocale.fullName, |
| |
64 buffer, result.getCapacity(), |
| |
65 &errorCode); |
| |
66 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
67 |
| |
68 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { |
| |
69 buffer=result.getBuffer(length); |
| |
70 if(buffer==0) { |
| |
71 result.truncate(0); |
| |
72 return result; |
| |
73 } |
| |
74 errorCode=U_ZERO_ERROR; |
| |
75 length=uloc_getDisplayLanguage(fullName, displayLocale.fullName, |
| |
76 buffer, result.getCapacity(), |
| |
77 &errorCode); |
| |
78 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
79 } |
| |
80 |
| |
81 return result; |
| |
82 } |
| |
83 |
| |
84 UnicodeString& |
| |
85 Locale::getDisplayScript(UnicodeString& dispScript) const |
| |
86 { |
| |
87 return this->getDisplayScript(getDefault(), dispScript); |
| |
88 } |
| |
89 |
| |
90 UnicodeString& |
| |
91 Locale::getDisplayScript(const Locale &displayLocale, |
| |
92 UnicodeString &result) const { |
| |
93 UChar *buffer; |
| |
94 UErrorCode errorCode=U_ZERO_ERROR; |
| |
95 int32_t length; |
| |
96 |
| |
97 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); |
| |
98 if(buffer==0) { |
| |
99 result.truncate(0); |
| |
100 return result; |
| |
101 } |
| |
102 |
| |
103 length=uloc_getDisplayScript(fullName, displayLocale.fullName, |
| |
104 buffer, result.getCapacity(), |
| |
105 &errorCode); |
| |
106 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
107 |
| |
108 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { |
| |
109 buffer=result.getBuffer(length); |
| |
110 if(buffer==0) { |
| |
111 result.truncate(0); |
| |
112 return result; |
| |
113 } |
| |
114 errorCode=U_ZERO_ERROR; |
| |
115 length=uloc_getDisplayScript(fullName, displayLocale.fullName, |
| |
116 buffer, result.getCapacity(), |
| |
117 &errorCode); |
| |
118 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
119 } |
| |
120 |
| |
121 return result; |
| |
122 } |
| |
123 |
| |
124 UnicodeString& |
| |
125 Locale::getDisplayCountry(UnicodeString& dispCntry) const |
| |
126 { |
| |
127 return this->getDisplayCountry(getDefault(), dispCntry); |
| |
128 } |
| |
129 |
| |
130 UnicodeString& |
| |
131 Locale::getDisplayCountry(const Locale &displayLocale, |
| |
132 UnicodeString &result) const { |
| |
133 UChar *buffer; |
| |
134 UErrorCode errorCode=U_ZERO_ERROR; |
| |
135 int32_t length; |
| |
136 |
| |
137 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); |
| |
138 if(buffer==0) { |
| |
139 result.truncate(0); |
| |
140 return result; |
| |
141 } |
| |
142 |
| |
143 length=uloc_getDisplayCountry(fullName, displayLocale.fullName, |
| |
144 buffer, result.getCapacity(), |
| |
145 &errorCode); |
| |
146 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
147 |
| |
148 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { |
| |
149 buffer=result.getBuffer(length); |
| |
150 if(buffer==0) { |
| |
151 result.truncate(0); |
| |
152 return result; |
| |
153 } |
| |
154 errorCode=U_ZERO_ERROR; |
| |
155 length=uloc_getDisplayCountry(fullName, displayLocale.fullName, |
| |
156 buffer, result.getCapacity(), |
| |
157 &errorCode); |
| |
158 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
159 } |
| |
160 |
| |
161 return result; |
| |
162 } |
| |
163 |
| |
164 UnicodeString& |
| |
165 Locale::getDisplayVariant(UnicodeString& dispVar) const |
| |
166 { |
| |
167 return this->getDisplayVariant(getDefault(), dispVar); |
| |
168 } |
| |
169 |
| |
170 UnicodeString& |
| |
171 Locale::getDisplayVariant(const Locale &displayLocale, |
| |
172 UnicodeString &result) const { |
| |
173 UChar *buffer; |
| |
174 UErrorCode errorCode=U_ZERO_ERROR; |
| |
175 int32_t length; |
| |
176 |
| |
177 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); |
| |
178 if(buffer==0) { |
| |
179 result.truncate(0); |
| |
180 return result; |
| |
181 } |
| |
182 |
| |
183 length=uloc_getDisplayVariant(fullName, displayLocale.fullName, |
| |
184 buffer, result.getCapacity(), |
| |
185 &errorCode); |
| |
186 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
187 |
| |
188 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { |
| |
189 buffer=result.getBuffer(length); |
| |
190 if(buffer==0) { |
| |
191 result.truncate(0); |
| |
192 return result; |
| |
193 } |
| |
194 errorCode=U_ZERO_ERROR; |
| |
195 length=uloc_getDisplayVariant(fullName, displayLocale.fullName, |
| |
196 buffer, result.getCapacity(), |
| |
197 &errorCode); |
| |
198 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
199 } |
| |
200 |
| |
201 return result; |
| |
202 } |
| |
203 |
| |
204 UnicodeString& |
| |
205 Locale::getDisplayName( UnicodeString& name ) const |
| |
206 { |
| |
207 return this->getDisplayName(getDefault(), name); |
| |
208 } |
| |
209 |
| |
210 UnicodeString& |
| |
211 Locale::getDisplayName(const Locale &displayLocale, |
| |
212 UnicodeString &result) const { |
| |
213 UChar *buffer; |
| |
214 UErrorCode errorCode=U_ZERO_ERROR; |
| |
215 int32_t length; |
| |
216 |
| |
217 buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); |
| |
218 if(buffer==0) { |
| |
219 result.truncate(0); |
| |
220 return result; |
| |
221 } |
| |
222 |
| |
223 length=uloc_getDisplayName(fullName, displayLocale.fullName, |
| |
224 buffer, result.getCapacity(), |
| |
225 &errorCode); |
| |
226 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
227 |
| |
228 if(errorCode==U_BUFFER_OVERFLOW_ERROR) { |
| |
229 buffer=result.getBuffer(length); |
| |
230 if(buffer==0) { |
| |
231 result.truncate(0); |
| |
232 return result; |
| |
233 } |
| |
234 errorCode=U_ZERO_ERROR; |
| |
235 length=uloc_getDisplayName(fullName, displayLocale.fullName, |
| |
236 buffer, result.getCapacity(), |
| |
237 &errorCode); |
| |
238 result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
| |
239 } |
| |
240 |
| |
241 return result; |
| |
242 } |
| |
243 |
| |
244 #if ! UCONFIG_NO_BREAK_ITERATION |
| |
245 |
| |
246 // ------------------------------------- |
| |
247 // Gets the objectLocale display name in the default locale language. |
| |
248 UnicodeString& U_EXPORT2 |
| |
249 BreakIterator::getDisplayName(const Locale& objectLocale, |
| |
250 UnicodeString& name) |
| |
251 { |
| |
252 return objectLocale.getDisplayName(name); |
| |
253 } |
| |
254 |
| |
255 // ------------------------------------- |
| |
256 // Gets the objectLocale display name in the displayLocale language. |
| |
257 UnicodeString& U_EXPORT2 |
| |
258 BreakIterator::getDisplayName(const Locale& objectLocale, |
| |
259 const Locale& displayLocale, |
| |
260 UnicodeString& name) |
| |
261 { |
| |
262 return objectLocale.getDisplayName(displayLocale, name); |
| |
263 } |
| |
264 |
| |
265 #endif |
| |
266 |
| |
267 |
| |
268 U_NAMESPACE_END |
| |
269 |
| |
270 // C API ------------------------------------------------------------------- *** |
| |
271 |
| |
272 U_NAMESPACE_USE |
| |
273 |
| |
274 /* ### Constants **************************************************/ |
| |
275 |
| |
276 /* These strings describe the resources we attempt to load from |
| |
277 the locale ResourceBundle data file.*/ |
| |
278 static const char _kLanguages[] = "Languages"; |
| |
279 static const char _kScripts[] = "Scripts"; |
| |
280 static const char _kScriptsStandAlone[] = "Scripts%stand-alone"; |
| |
281 static const char _kCountries[] = "Countries"; |
| |
282 static const char _kVariants[] = "Variants"; |
| |
283 static const char _kKeys[] = "Keys"; |
| |
284 static const char _kTypes[] = "Types"; |
| |
285 //static const char _kRootName[] = "root"; |
| |
286 static const char _kCurrency[] = "currency"; |
| |
287 static const char _kCurrencies[] = "Currencies"; |
| |
288 static const char _kLocaleDisplayPattern[] = "localeDisplayPattern"; |
| |
289 static const char _kPattern[] = "pattern"; |
| |
290 static const char _kSeparator[] = "separator"; |
| |
291 |
| |
292 /* ### Display name **************************************************/ |
| |
293 |
| |
294 static int32_t |
| |
295 _getStringOrCopyKey(const char *path, const char *locale, |
| |
296 const char *tableKey, |
| |
297 const char* subTableKey, |
| |
298 const char *itemKey, |
| |
299 const char *substitute, |
| |
300 UChar *dest, int32_t destCapacity, |
| |
301 UErrorCode *pErrorCode) { |
| |
302 const UChar *s = NULL; |
| |
303 int32_t length = 0; |
| |
304 |
| |
305 if(itemKey==NULL) { |
| |
306 /* top-level item: normal resource bundle access */ |
| |
307 UResourceBundle *rb; |
| |
308 |
| |
309 rb=ures_open(path, locale, pErrorCode); |
| |
310 |
| |
311 if(U_SUCCESS(*pErrorCode)) { |
| |
312 s=ures_getStringByKey(rb, tableKey, &length, pErrorCode); |
| |
313 /* see comment about closing rb near "return item;" in _res_getTableStringWithFallback() */ |
| |
314 ures_close(rb); |
| |
315 } |
| |
316 } else { |
| |
317 /* Language code should not be a number. If it is, set the error code. */ |
| |
318 if (!uprv_strncmp(tableKey, "Languages", 9) && uprv_strtol(itemKey, NULL, 10)) { |
| |
319 *pErrorCode = U_MISSING_RESOURCE_ERROR; |
| |
320 } else { |
| |
321 /* second-level item, use special fallback */ |
| |
322 s=uloc_getTableStringWithFallback(path, locale, |
| |
323 tableKey, |
| |
324 subTableKey, |
| |
325 itemKey, |
| |
326 &length, |
| |
327 pErrorCode); |
| |
328 } |
| |
329 } |
| |
330 |
| |
331 if(U_SUCCESS(*pErrorCode)) { |
| |
332 int32_t copyLength=uprv_min(length, destCapacity); |
| |
333 if(copyLength>0 && s != NULL) { |
| |
334 u_memcpy(dest, s, copyLength); |
| |
335 } |
| |
336 } else { |
| |
337 /* no string from a resource bundle: convert the substitute */ |
| |
338 length=(int32_t)uprv_strlen(substitute); |
| |
339 u_charsToUChars(substitute, dest, uprv_min(length, destCapacity)); |
| |
340 *pErrorCode=U_USING_DEFAULT_WARNING; |
| |
341 } |
| |
342 |
| |
343 return u_terminateUChars(dest, destCapacity, length, pErrorCode); |
| |
344 } |
| |
345 |
| |
346 typedef int32_t U_CALLCONV UDisplayNameGetter(const char *, char *, int32_t, UErrorCode *); |
| |
347 |
| |
348 static int32_t |
| |
349 _getDisplayNameForComponent(const char *locale, |
| |
350 const char *displayLocale, |
| |
351 UChar *dest, int32_t destCapacity, |
| |
352 UDisplayNameGetter *getter, |
| |
353 const char *tag, |
| |
354 UErrorCode *pErrorCode) { |
| |
355 char localeBuffer[ULOC_FULLNAME_CAPACITY*4]; |
| |
356 int32_t length; |
| |
357 UErrorCode localStatus; |
| |
358 const char* root = NULL; |
| |
359 |
| |
360 /* argument checking */ |
| |
361 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { |
| |
362 return 0; |
| |
363 } |
| |
364 |
| |
365 if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { |
| |
366 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; |
| |
367 return 0; |
| |
368 } |
| |
369 |
| |
370 localStatus = U_ZERO_ERROR; |
| |
371 length=(*getter)(locale, localeBuffer, sizeof(localeBuffer), &localStatus); |
| |
372 if(U_FAILURE(localStatus) || localStatus==U_STRING_NOT_TERMINATED_WARNING) { |
| |
373 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; |
| |
374 return 0; |
| |
375 } |
| |
376 if(length==0) { |
| |
377 return u_terminateUChars(dest, destCapacity, 0, pErrorCode); |
| |
378 } |
| |
379 |
| |
380 root = tag == _kCountries ? U_ICUDATA_REGION : U_ICUDATA_LANG; |
| |
381 |
| |
382 return _getStringOrCopyKey(root, displayLocale, |
| |
383 tag, NULL, localeBuffer, |
| |
384 localeBuffer, |
| |
385 dest, destCapacity, |
| |
386 pErrorCode); |
| |
387 } |
| |
388 |
| |
389 U_CAPI int32_t U_EXPORT2 |
| |
390 uloc_getDisplayLanguage(const char *locale, |
| |
391 const char *displayLocale, |
| |
392 UChar *dest, int32_t destCapacity, |
| |
393 UErrorCode *pErrorCode) { |
| |
394 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, |
| |
395 uloc_getLanguage, _kLanguages, pErrorCode); |
| |
396 } |
| |
397 |
| |
398 U_CAPI int32_t U_EXPORT2 |
| |
399 uloc_getDisplayScript(const char* locale, |
| |
400 const char* displayLocale, |
| |
401 UChar *dest, int32_t destCapacity, |
| |
402 UErrorCode *pErrorCode) |
| |
403 { |
| |
404 UErrorCode err = U_ZERO_ERROR; |
| |
405 int32_t res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, |
| |
406 uloc_getScript, _kScriptsStandAlone, &err); |
| |
407 |
| |
408 if ( err == U_USING_DEFAULT_WARNING ) { |
| |
409 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, |
| |
410 uloc_getScript, _kScripts, pErrorCode); |
| |
411 } else { |
| |
412 *pErrorCode = err; |
| |
413 return res; |
| |
414 } |
| |
415 } |
| |
416 |
| |
417 U_INTERNAL int32_t U_EXPORT2 |
| |
418 uloc_getDisplayScriptInContext(const char* locale, |
| |
419 const char* displayLocale, |
| |
420 UChar *dest, int32_t destCapacity, |
| |
421 UErrorCode *pErrorCode) |
| |
422 { |
| |
423 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, |
| |
424 uloc_getScript, _kScripts, pErrorCode); |
| |
425 } |
| |
426 |
| |
427 U_CAPI int32_t U_EXPORT2 |
| |
428 uloc_getDisplayCountry(const char *locale, |
| |
429 const char *displayLocale, |
| |
430 UChar *dest, int32_t destCapacity, |
| |
431 UErrorCode *pErrorCode) { |
| |
432 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, |
| |
433 uloc_getCountry, _kCountries, pErrorCode); |
| |
434 } |
| |
435 |
| |
436 /* |
| |
437 * TODO separate variant1_variant2_variant3... |
| |
438 * by getting each tag's display string and concatenating them with ", " |
| |
439 * in between - similar to uloc_getDisplayName() |
| |
440 */ |
| |
441 U_CAPI int32_t U_EXPORT2 |
| |
442 uloc_getDisplayVariant(const char *locale, |
| |
443 const char *displayLocale, |
| |
444 UChar *dest, int32_t destCapacity, |
| |
445 UErrorCode *pErrorCode) { |
| |
446 return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, |
| |
447 uloc_getVariant, _kVariants, pErrorCode); |
| |
448 } |
| |
449 |
| |
450 /* Instead of having a separate pass for 'special' patterns, reintegrate the two |
| |
451 * so we don't get bitten by preflight bugs again. We can be reasonably efficient |
| |
452 * without two separate code paths, this code isn't that performance-critical. |
| |
453 * |
| |
454 * This code is general enough to deal with patterns that have a prefix or swap the |
| |
455 * language and remainder components, since we gave developers enough rope to do such |
| |
456 * things if they futz with the pattern data. But since we don't give them a way to |
| |
457 * specify a pattern for arbitrary combinations of components, there's not much use in |
| |
458 * that. I don't think our data includes such patterns, the only variable I know if is |
| |
459 * whether there is a space before the open paren, or not. Oh, and zh uses different |
| |
460 * chars than the standard open/close paren (which ja and ko use, btw). |
| |
461 */ |
| |
462 U_CAPI int32_t U_EXPORT2 |
| |
463 uloc_getDisplayName(const char *locale, |
| |
464 const char *displayLocale, |
| |
465 UChar *dest, int32_t destCapacity, |
| |
466 UErrorCode *pErrorCode) |
| |
467 { |
| |
468 static const UChar defaultSeparator[9] = { 0x007b, 0x0030, 0x007d, 0x002c, 0x0020, 0x007b, 0x0031, 0x007d, 0x0000 }; /* "{0}, {1}" */ |
| |
469 static const UChar sub0[4] = { 0x007b, 0x0030, 0x007d , 0x0000 } ; /* {0} */ |
| |
470 static const UChar sub1[4] = { 0x007b, 0x0031, 0x007d , 0x0000 } ; /* {1} */ |
| |
471 static const int32_t subLen = 3; |
| |
472 static const UChar defaultPattern[10] = { |
| |
473 0x007b, 0x0030, 0x007d, 0x0020, 0x0028, 0x007b, 0x0031, 0x007d, 0x0029, 0x0000 |
| |
474 }; /* {0} ({1}) */ |
| |
475 static const int32_t defaultPatLen = 9; |
| |
476 static const int32_t defaultSub0Pos = 0; |
| |
477 static const int32_t defaultSub1Pos = 5; |
| |
478 |
| |
479 int32_t length; /* of formatted result */ |
| |
480 |
| |
481 const UChar *separator; |
| |
482 int32_t sepLen = 0; |
| |
483 const UChar *pattern; |
| |
484 int32_t patLen = 0; |
| |
485 int32_t sub0Pos, sub1Pos; |
| |
486 |
| |
487 UChar formatOpenParen = 0x0028; // ( |
| |
488 UChar formatReplaceOpenParen = 0x005B; // [ |
| |
489 UChar formatCloseParen = 0x0029; // ) |
| |
490 UChar formatReplaceCloseParen = 0x005D; // ] |
| |
491 |
| |
492 UBool haveLang = TRUE; /* assume true, set false if we find we don't have |
| |
493 a lang component in the locale */ |
| |
494 UBool haveRest = TRUE; /* assume true, set false if we find we don't have |
| |
495 any other component in the locale */ |
| |
496 UBool retry = FALSE; /* set true if we need to retry, see below */ |
| |
497 |
| |
498 int32_t langi = 0; /* index of the language substitution (0 or 1), virtually always 0 */ |
| |
499 |
| |
500 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { |
| |
501 return 0; |
| |
502 } |
| |
503 |
| |
504 if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { |
| |
505 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; |
| |
506 return 0; |
| |
507 } |
| |
508 |
| |
509 { |
| |
510 UErrorCode status = U_ZERO_ERROR; |
| |
511 UResourceBundle* locbundle=ures_open(U_ICUDATA_LANG, displayLocale, &status); |
| |
512 UResourceBundle* dspbundle=ures_getByKeyWithFallback(locbundle, _kLocaleDisplayPattern, |
| |
513 NULL, &status); |
| |
514 |
| |
515 separator=ures_getStringByKeyWithFallback(dspbundle, _kSeparator, &sepLen, &status); |
| |
516 pattern=ures_getStringByKeyWithFallback(dspbundle, _kPattern, &patLen, &status); |
| |
517 |
| |
518 ures_close(dspbundle); |
| |
519 ures_close(locbundle); |
| |
520 } |
| |
521 |
| |
522 /* If we couldn't find any data, then use the defaults */ |
| |
523 if(sepLen == 0) { |
| |
524 separator = defaultSeparator; |
| |
525 } |
| |
526 /* #10244: Even though separator is now a pattern, it is awkward to handle it as such |
| |
527 * here since we are trying to build the display string in place in the dest buffer, |
| |
528 * and to handle it as a pattern would entail having separate storage for the |
| |
529 * substrings that need to be combined (the first of which may be the result of |
| |
530 * previous such combinations). So for now we continue to treat the portion between |
| |
531 * {0} and {1} as a string to be appended when joining substrings, ignoring anything |
| |
532 * that is before {0} or after {1} (no existing separator pattern has any such thing). |
| |
533 * This is similar to how pattern is handled below. |
| |
534 */ |
| |
535 { |
| |
536 UChar *p0=u_strstr(separator, sub0); |
| |
537 UChar *p1=u_strstr(separator, sub1); |
| |
538 if (p0==NULL || p1==NULL || p1<p0) { |
| |
539 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; |
| |
540 return 0; |
| |
541 } |
| |
542 separator = (const UChar *)p0 + subLen; |
| |
543 sepLen = p1 - separator; |
| |
544 } |
| |
545 |
| |
546 if(patLen==0 || (patLen==defaultPatLen && !u_strncmp(pattern, defaultPattern, patLen))) { |
| |
547 pattern=defaultPattern; |
| |
548 patLen=defaultPatLen; |
| |
549 sub0Pos=defaultSub0Pos; |
| |
550 sub1Pos=defaultSub1Pos; |
| |
551 // use default formatOpenParen etc. set above |
| |
552 } else { /* non-default pattern */ |
| |
553 UChar *p0=u_strstr(pattern, sub0); |
| |
554 UChar *p1=u_strstr(pattern, sub1); |
| |
555 if (p0==NULL || p1==NULL) { |
| |
556 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; |
| |
557 return 0; |
| |
558 } |
| |
559 sub0Pos=p0-pattern; |
| |
560 sub1Pos=p1-pattern; |
| |
561 if (sub1Pos < sub0Pos) { /* a very odd pattern */ |
| |
562 int32_t t=sub0Pos; sub0Pos=sub1Pos; sub1Pos=t; |
| |
563 langi=1; |
| |
564 } |
| |
565 if (u_strchr(pattern, 0xFF08) != NULL) { |
| |
566 formatOpenParen = 0xFF08; // fullwidth ( |
| |
567 formatReplaceOpenParen = 0xFF3B; // fullwidth [ |
| |
568 formatCloseParen = 0xFF09; // fullwidth ) |
| |
569 formatReplaceCloseParen = 0xFF3D; // fullwidth ] |
| |
570 } |
| |
571 } |
| |
572 |
| |
573 /* We loop here because there is one case in which after the first pass we could need to |
| |
574 * reextract the data. If there's initial padding before the first element, we put in |
| |
575 * the padding and then write that element. If it turns out there's no second element, |
| |
576 * we didn't need the padding. If we do need the data (no preflight), and the first element |
| |
577 * would have fit but for the padding, we need to reextract. In this case (only) we |
| |
578 * adjust the parameters so padding is not added, and repeat. |
| |
579 */ |
| |
580 do { |
| |
581 UChar* p=dest; |
| |
582 int32_t patPos=0; /* position in the pattern, used for non-substitution portions */ |
| |
583 int32_t langLen=0; /* length of language substitution */ |
| |
584 int32_t langPos=0; /* position in output of language substitution */ |
| |
585 int32_t restLen=0; /* length of 'everything else' substitution */ |
| |
586 int32_t restPos=0; /* position in output of 'everything else' substitution */ |
| |
587 UEnumeration* kenum = NULL; /* keyword enumeration */ |
| |
588 |
| |
589 /* prefix of pattern, extremely likely to be empty */ |
| |
590 if(sub0Pos) { |
| |
591 if(destCapacity >= sub0Pos) { |
| |
592 while (patPos < sub0Pos) { |
| |
593 *p++ = pattern[patPos++]; |
| |
594 } |
| |
595 } else { |
| |
596 patPos=sub0Pos; |
| |
597 } |
| |
598 length=sub0Pos; |
| |
599 } else { |
| |
600 length=0; |
| |
601 } |
| |
602 |
| |
603 for(int32_t subi=0,resti=0;subi<2;) { /* iterate through patterns 0 and 1*/ |
| |
604 UBool subdone = FALSE; /* set true when ready to move to next substitution */ |
| |
605 |
| |
606 /* prep p and cap for calls to get display components, pin cap to 0 since |
| |
607 they complain if cap is negative */ |
| |
608 int32_t cap=destCapacity-length; |
| |
609 if (cap <= 0) { |
| |
610 cap=0; |
| |
611 } else { |
| |
612 p=dest+length; |
| |
613 } |
| |
614 |
| |
615 if (subi == langi) { /* {0}*/ |
| |
616 if(haveLang) { |
| |
617 langPos=length; |
| |
618 langLen=uloc_getDisplayLanguage(locale, displayLocale, p, cap, pErrorCode); |
| |
619 length+=langLen; |
| |
620 haveLang=langLen>0; |
| |
621 } |
| |
622 subdone=TRUE; |
| |
623 } else { /* {1} */ |
| |
624 if(!haveRest) { |
| |
625 subdone=TRUE; |
| |
626 } else { |
| |
627 int32_t len; /* length of component (plus other stuff) we just fetched */ |
| |
628 switch(resti++) { |
| |
629 case 0: |
| |
630 restPos=length; |
| |
631 len=uloc_getDisplayScriptInContext(locale, displayLocale, p, cap, pErrorCode); |
| |
632 break; |
| |
633 case 1: |
| |
634 len=uloc_getDisplayCountry(locale, displayLocale, p, cap, pErrorCode); |
| |
635 break; |
| |
636 case 2: |
| |
637 len=uloc_getDisplayVariant(locale, displayLocale, p, cap, pErrorCode); |
| |
638 break; |
| |
639 case 3: |
| |
640 kenum = uloc_openKeywords(locale, pErrorCode); |
| |
641 /* fall through */ |
| |
642 default: { |
| |
643 const char* kw=uenum_next(kenum, &len, pErrorCode); |
| |
644 if (kw == NULL) { |
| |
645 uenum_close(kenum); |
| |
646 len=0; /* mark that we didn't add a component */ |
| |
647 subdone=TRUE; |
| |
648 } else { |
| |
649 /* incorporating this behavior into the loop made it even more complex, |
| |
650 so just special case it here */ |
| |
651 len = uloc_getDisplayKeyword(kw, displayLocale, p, cap, pErrorCode); |
| |
652 if(len) { |
| |
653 if(len < cap) { |
| |
654 p[len]=0x3d; /* '=', assume we'll need it */ |
| |
655 } |
| |
656 len+=1; |
| |
657 |
| |
658 /* adjust for call to get keyword */ |
| |
659 cap-=len; |
| |
660 if(cap <= 0) { |
| |
661 cap=0; |
| |
662 } else { |
| |
663 p+=len; |
| |
664 } |
| |
665 } |
| |
666 /* reset for call below */ |
| |
667 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) { |
| |
668 *pErrorCode=U_ZERO_ERROR; |
| |
669 } |
| |
670 int32_t vlen = uloc_getDisplayKeywordValue(locale, kw, displayLocale, |
| |
671 p, cap, pErrorCode); |
| |
672 if(len) { |
| |
673 if(vlen==0) { |
| |
674 --len; /* remove unneeded '=' */ |
| |
675 } |
| |
676 /* restore cap and p to what they were at start */ |
| |
677 cap=destCapacity-length; |
| |
678 if(cap <= 0) { |
| |
679 cap=0; |
| |
680 } else { |
| |
681 p=dest+length; |
| |
682 } |
| |
683 } |
| |
684 len+=vlen; /* total we added for key + '=' + value */ |
| |
685 } |
| |
686 } break; |
| |
687 } /* end switch */ |
| |
688 |
| |
689 if (len>0) { |
| |
690 /* we addeed a component, so add separator and write it if there's room. */ |
| |
691 if(len+sepLen<=cap) { |
| |
692 const UChar * plimit = p + len; |
| |
693 for (; p < plimit; p++) { |
| |
694 if (*p == formatOpenParen) { |
| |
695 *p = formatReplaceOpenParen; |
| |
696 } else if (*p == formatCloseParen) { |
| |
697 *p = formatReplaceCloseParen; |
| |
698 } |
| |
699 } |
| |
700 for(int32_t i=0;i<sepLen;++i) { |
| |
701 *p++=separator[i]; |
| |
702 } |
| |
703 } |
| |
704 length+=len+sepLen; |
| |
705 } else if(subdone) { |
| |
706 /* remove separator if we added it */ |
| |
707 if (length!=restPos) { |
| |
708 length-=sepLen; |
| |
709 } |
| |
710 restLen=length-restPos; |
| |
711 haveRest=restLen>0; |
| |
712 } |
| |
713 } |
| |
714 } |
| |
715 |
| |
716 if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) { |
| |
717 *pErrorCode=U_ZERO_ERROR; |
| |
718 } |
| |
719 |
| |
720 if(subdone) { |
| |
721 if(haveLang && haveRest) { |
| |
722 /* append internal portion of pattern, the first time, |
| |
723 or last portion of pattern the second time */ |
| |
724 int32_t padLen; |
| |
725 patPos+=subLen; |
| |
726 padLen=(subi==0 ? sub1Pos : patLen)-patPos; |
| |
727 if(length+padLen < destCapacity) { |
| |
728 p=dest+length; |
| |
729 for(int32_t i=0;i<padLen;++i) { |
| |
730 *p++=pattern[patPos++]; |
| |
731 } |
| |
732 } else { |
| |
733 patPos+=padLen; |
| |
734 } |
| |
735 length+=padLen; |
| |
736 } else if(subi==0) { |
| |
737 /* don't have first component, reset for second component */ |
| |
738 sub0Pos=0; |
| |
739 length=0; |
| |
740 } else if(length>0) { |
| |
741 /* true length is the length of just the component we got. */ |
| |
742 length=haveLang?langLen:restLen; |
| |
743 if(dest && sub0Pos!=0) { |
| |
744 if (sub0Pos+length<=destCapacity) { |
| |
745 /* first component not at start of result, |
| |
746 but we have full component in buffer. */ |
| |
747 u_memmove(dest, dest+(haveLang?langPos:restPos), length); |
| |
748 } else { |
| |
749 /* would have fit, but didn't because of pattern prefix. */ |
| |
750 sub0Pos=0; /* stops initial padding (and a second retry, |
| |
751 so we won't end up here again) */ |
| |
752 retry=TRUE; |
| |
753 } |
| |
754 } |
| |
755 } |
| |
756 |
| |
757 ++subi; /* move on to next substitution */ |
| |
758 } |
| |
759 } |
| |
760 } while(retry); |
| |
761 |
| |
762 return u_terminateUChars(dest, destCapacity, length, pErrorCode); |
| |
763 } |
| |
764 |
| |
765 U_CAPI int32_t U_EXPORT2 |
| |
766 uloc_getDisplayKeyword(const char* keyword, |
| |
767 const char* displayLocale, |
| |
768 UChar* dest, |
| |
769 int32_t destCapacity, |
| |
770 UErrorCode* status){ |
| |
771 |
| |
772 /* argument checking */ |
| |
773 if(status==NULL || U_FAILURE(*status)) { |
| |
774 return 0; |
| |
775 } |
| |
776 |
| |
777 if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { |
| |
778 *status=U_ILLEGAL_ARGUMENT_ERROR; |
| |
779 return 0; |
| |
780 } |
| |
781 |
| |
782 |
| |
783 /* pass itemKey=NULL to look for a top-level item */ |
| |
784 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale, |
| |
785 _kKeys, NULL, |
| |
786 keyword, |
| |
787 keyword, |
| |
788 dest, destCapacity, |
| |
789 status); |
| |
790 |
| |
791 } |
| |
792 |
| |
793 |
| |
794 #define UCURRENCY_DISPLAY_NAME_INDEX 1 |
| |
795 |
| |
796 U_CAPI int32_t U_EXPORT2 |
| |
797 uloc_getDisplayKeywordValue( const char* locale, |
| |
798 const char* keyword, |
| |
799 const char* displayLocale, |
| |
800 UChar* dest, |
| |
801 int32_t destCapacity, |
| |
802 UErrorCode* status){ |
| |
803 |
| |
804 |
| |
805 char keywordValue[ULOC_FULLNAME_CAPACITY*4]; |
| |
806 int32_t capacity = ULOC_FULLNAME_CAPACITY*4; |
| |
807 int32_t keywordValueLen =0; |
| |
808 |
| |
809 /* argument checking */ |
| |
810 if(status==NULL || U_FAILURE(*status)) { |
| |
811 return 0; |
| |
812 } |
| |
813 |
| |
814 if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { |
| |
815 *status=U_ILLEGAL_ARGUMENT_ERROR; |
| |
816 return 0; |
| |
817 } |
| |
818 |
| |
819 /* get the keyword value */ |
| |
820 keywordValue[0]=0; |
| |
821 keywordValueLen = uloc_getKeywordValue(locale, keyword, keywordValue, capacity, status); |
| |
822 |
| |
823 /* |
| |
824 * if the keyword is equal to currency .. then to get the display name |
| |
825 * we need to do the fallback ourselves |
| |
826 */ |
| |
827 if(uprv_stricmp(keyword, _kCurrency)==0){ |
| |
828 |
| |
829 int32_t dispNameLen = 0; |
| |
830 const UChar *dispName = NULL; |
| |
831 |
| |
832 UResourceBundle *bundle = ures_open(U_ICUDATA_CURR, displayLocale, status); |
| |
833 UResourceBundle *currencies = ures_getByKey(bundle, _kCurrencies, NULL, status); |
| |
834 UResourceBundle *currency = ures_getByKeyWithFallback(currencies, keywordValue, NULL, status); |
| |
835 |
| |
836 dispName = ures_getStringByIndex(currency, UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status); |
| |
837 |
| |
838 /*close the bundles */ |
| |
839 ures_close(currency); |
| |
840 ures_close(currencies); |
| |
841 ures_close(bundle); |
| |
842 |
| |
843 if(U_FAILURE(*status)){ |
| |
844 if(*status == U_MISSING_RESOURCE_ERROR){ |
| |
845 /* we just want to write the value over if nothing is available */ |
| |
846 *status = U_USING_DEFAULT_WARNING; |
| |
847 }else{ |
| |
848 return 0; |
| |
849 } |
| |
850 } |
| |
851 |
| |
852 /* now copy the dispName over if not NULL */ |
| |
853 if(dispName != NULL){ |
| |
854 if(dispNameLen <= destCapacity){ |
| |
855 uprv_memcpy(dest, dispName, dispNameLen * U_SIZEOF_UCHAR); |
| |
856 return u_terminateUChars(dest, destCapacity, dispNameLen, status); |
| |
857 }else{ |
| |
858 *status = U_BUFFER_OVERFLOW_ERROR; |
| |
859 return dispNameLen; |
| |
860 } |
| |
861 }else{ |
| |
862 /* we have not found the display name for the value .. just copy over */ |
| |
863 if(keywordValueLen <= destCapacity){ |
| |
864 u_charsToUChars(keywordValue, dest, keywordValueLen); |
| |
865 return u_terminateUChars(dest, destCapacity, keywordValueLen, status); |
| |
866 }else{ |
| |
867 *status = U_BUFFER_OVERFLOW_ERROR; |
| |
868 return keywordValueLen; |
| |
869 } |
| |
870 } |
| |
871 |
| |
872 |
| |
873 }else{ |
| |
874 |
| |
875 return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale, |
| |
876 _kTypes, keyword, |
| |
877 keywordValue, |
| |
878 keywordValue, |
| |
879 dest, destCapacity, |
| |
880 status); |
| |
881 } |
| |
882 } |