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