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