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