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