1 /*
2 * Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "ICUCategoryData.h"
8
9 #include <string.h>
10
11 #include <unicode/uchar.h>
12
13
14 U_NAMESPACE_USE
15
16
17 namespace BPrivate {
18 namespace Libroot {
19
20
ICUCategoryData(pthread_key_t tlsKey)21 ICUCategoryData::ICUCategoryData(pthread_key_t tlsKey)
22 :
23 fThreadLocalStorageKey(tlsKey)
24 {
25 *fPosixLocaleName = '\0';
26 *fGivenCharset = '\0';
27 }
28
29
~ICUCategoryData()30 ICUCategoryData::~ICUCategoryData()
31 {
32 }
33
34
35 status_t
SetTo(const Locale & locale,const char * posixLocaleName)36 ICUCategoryData::SetTo(const Locale& locale, const char* posixLocaleName)
37 {
38 if (!posixLocaleName)
39 return B_BAD_VALUE;
40
41 fLocale = locale;
42 strlcpy(fPosixLocaleName, posixLocaleName, skMaxPosixLocaleNameLen);
43 *fGivenCharset = '\0';
44
45 // POSIX locales often contain an embedded charset, but ICU does not
46 // handle these within locales (that part of the name is simply
47 // ignored).
48 // We need to fetch the charset specification and lookup an appropriate
49 // ICU charset converter. This converter will later be used to get info
50 // about ctype properties.
51 const char* charsetStart = strchr(fPosixLocaleName, '.');
52 if (charsetStart) {
53 ++charsetStart;
54 int l = 0;
55 while (charsetStart[l] != '\0' && charsetStart[l] != '@')
56 ++l;
57 snprintf(fGivenCharset, UCNV_MAX_CONVERTER_NAME_LENGTH, "%.*s", l,
58 charsetStart);
59 }
60 if (!strlen(fGivenCharset))
61 strcpy(fGivenCharset, "utf-8");
62
63 return _SetupConverter();
64 }
65
66
67 status_t
SetToPosix()68 ICUCategoryData::SetToPosix()
69 {
70 fLocale = Locale::createFromName("en_US_POSIX");
71 strcpy(fPosixLocaleName, "POSIX");
72 strcpy(fGivenCharset, "US-ASCII");
73
74 return _SetupConverter();
75 }
76
77
78 status_t
_ConvertUnicodeStringToLocaleconvEntry(const UnicodeString & string,char * destination,int destinationSize,const char * defaultValue)79 ICUCategoryData::_ConvertUnicodeStringToLocaleconvEntry(
80 const UnicodeString& string, char* destination, int destinationSize,
81 const char* defaultValue)
82 {
83 UConverter* converter;
84 status_t result = _GetConverter(converter);
85 if (result != B_OK)
86 return result;
87
88 UErrorCode icuStatus = U_ZERO_ERROR;
89 ucnv_fromUChars(converter, destination, destinationSize, string.getBuffer(),
90 string.length(), &icuStatus);
91 if (!U_SUCCESS(icuStatus)) {
92 switch (icuStatus) {
93 case U_BUFFER_OVERFLOW_ERROR:
94 result = B_NAME_TOO_LONG;
95 break;
96 case U_INVALID_CHAR_FOUND:
97 case U_TRUNCATED_CHAR_FOUND:
98 case U_ILLEGAL_CHAR_FOUND:
99 result = B_BAD_DATA;
100 break;
101 default:
102 result = B_ERROR;
103 break;
104 }
105 strlcpy(destination, defaultValue, destinationSize);
106 }
107
108 return result;
109 }
110
111
112 status_t
_GetConverter(UConverter * & converterOut)113 ICUCategoryData::_GetConverter(UConverter*& converterOut)
114 {
115 // we use different converters per thread to avoid concurrent accesses
116 ICUThreadLocalStorageValue* tlsValue = NULL;
117 status_t result = ICUThreadLocalStorageValue::GetInstanceForKey(
118 fThreadLocalStorageKey, tlsValue);
119 if (result != B_OK)
120 return result;
121
122 if (tlsValue->converter != NULL) {
123 if (strcmp(tlsValue->charset, fGivenCharset) == 0) {
124 converterOut = tlsValue->converter;
125 return B_OK;
126 }
127
128 // charset no longer matches the converter, we need to dump it and
129 // create a new one
130 ucnv_close(tlsValue->converter);
131 tlsValue->converter = NULL;
132 }
133
134 // create a new converter for the current charset
135 UErrorCode icuStatus = U_ZERO_ERROR;
136 UConverter* icuConverter = ucnv_open(fGivenCharset, &icuStatus);
137 if (icuConverter == NULL)
138 return B_NAME_NOT_FOUND;
139
140 // setup the new converter to stop upon any errors
141 icuStatus = U_ZERO_ERROR;
142 ucnv_setToUCallBack(icuConverter, UCNV_TO_U_CALLBACK_STOP, NULL, NULL, NULL,
143 &icuStatus);
144 if (!U_SUCCESS(icuStatus)) {
145 ucnv_close(icuConverter);
146 return B_ERROR;
147 }
148 icuStatus = U_ZERO_ERROR;
149 ucnv_setFromUCallBack(icuConverter, UCNV_FROM_U_CALLBACK_STOP, NULL, NULL,
150 NULL, &icuStatus);
151 if (!U_SUCCESS(icuStatus)) {
152 ucnv_close(icuConverter);
153 return B_ERROR;
154 }
155
156 tlsValue->converter = icuConverter;
157 strlcpy(tlsValue->charset, fGivenCharset, sizeof(tlsValue->charset));
158
159 converterOut = icuConverter;
160
161 return B_OK;
162 }
163
164
165 status_t
_SetupConverter()166 ICUCategoryData::_SetupConverter()
167 {
168 UConverter* converter;
169 return _GetConverter(converter);
170 }
171
172
173 } // namespace Libroot
174 } // namespace BPrivate
175