xref: /haiku/src/system/libroot/add-ons/icu/ICUCategoryData.cpp (revision fccd8899fcb583bfb73c5c26c9fcd714b963959b)
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 	}
129 
130 	// create a new converter for the current charset
131 	UErrorCode icuStatus = U_ZERO_ERROR;
132 	UConverter* icuConverter = ucnv_open(fGivenCharset, &icuStatus);
133 	if (icuConverter == NULL)
134 		return B_NAME_NOT_FOUND;
135 
136 	// setup the new converter to stop upon any errors
137 	icuStatus = U_ZERO_ERROR;
138 	ucnv_setToUCallBack(icuConverter, UCNV_TO_U_CALLBACK_STOP, NULL, NULL, NULL,
139 		&icuStatus);
140 	if (!U_SUCCESS(icuStatus)) {
141 		ucnv_close(icuConverter);
142 		return B_ERROR;
143 	}
144 	icuStatus = U_ZERO_ERROR;
145 	ucnv_setFromUCallBack(icuConverter, UCNV_FROM_U_CALLBACK_STOP, NULL, NULL,
146 		NULL, &icuStatus);
147 	if (!U_SUCCESS(icuStatus)) {
148 		ucnv_close(icuConverter);
149 		return B_ERROR;
150 	}
151 
152 	tlsValue->converter = icuConverter;
153 	strlcpy(tlsValue->charset, fGivenCharset, sizeof(tlsValue->charset));
154 
155 	converterOut = icuConverter;
156 
157 	return B_OK;
158 }
159 
160 
161 status_t
162 ICUCategoryData::_SetupConverter()
163 {
164 	UConverter* converter;
165 	return _GetConverter(converter);
166 }
167 
168 
169 }	// namespace Libroot
170 }	// namespace BPrivate
171