xref: /haiku/src/system/libroot/add-ons/icu/ICUCategoryData.cpp (revision 6011ce6c7495e4e707bd33b12a7e22d66c710aad)
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 
21 ICUCategoryData::ICUCategoryData(pthread_key_t tlsKey)
22 	:
23 	fThreadLocalStorageKey(tlsKey)
24 {
25 	*fPosixLocaleName = '\0';
26 	*fGivenCharset = '\0';
27 }
28 
29 
30 ICUCategoryData::~ICUCategoryData()
31 {
32 }
33 
34 
35 status_t
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
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
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
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
166 ICUCategoryData::_SetupConverter()
167 {
168 	UConverter* converter;
169 	return _GetConverter(converter);
170 }
171 
172 
173 }	// namespace Libroot
174 }	// namespace BPrivate
175