1/* 2***************************************************************************************** 3* Copyright (C) 2014 Apple Inc. All Rights Reserved. 4***************************************************************************************** 5*/ 6 7#include "unicode/utypes.h" 8#include "unicode/ualoc.h" 9#include "unicode/uloc.h" 10#include "unicode/ures.h" 11#include "unicode/putil.h" 12#include "cstring.h" 13#include "cmemory.h" 14// the following has replacements for some math.h funcs etc 15#include "putilimp.h" 16 17 18// The numeric values in territoryInfo are in "IntF" format from LDML2ICUConverter. 19// From its docs (adapted): [IntF is] a special integer that represents the number in 20// normalized scientific notation. 21// Resultant integers are in the form -?xxyyyyyy, where xx is the exponent 22// offset by 50 and yyyyyy is the coefficient to 5 decimal places (range 1.0 to 9.99999), e.g. 23// 14660000000000 -> 1.46600E13 -> 63146600 24// 0.0001 -> 1.00000E-4 -> 46100000 25// -123.456 -> -1.23456E-2 -> -48123456 26// 27// Here to avoid an extra division we have the max coefficient as 999999 (instead of 28// 9.99999) and instead offset the exponent by -55. 29// 30static double doubleFromIntF(int32_t intF) { 31 double coefficient = (double)(intF % 1000000); 32 int32_t exponent = (intF / 1000000) - 55; 33 return coefficient * uprv_pow10(exponent); 34} 35 36static int compareLangEntries(const void * entry1, const void * entry2) { 37 double fraction1 = ((const UALanguageEntry *)entry1)->userFraction; 38 double fraction2 = ((const UALanguageEntry *)entry2)->userFraction; 39 // want descending order 40 if (fraction1 > fraction2) return -1; 41 if (fraction1 < fraction2) return 1; 42 // userFractions the same, sort by languageCode 43 return uprv_strcmp(((const UALanguageEntry *)entry1)->languageCode,((const UALanguageEntry *)entry2)->languageCode); 44} 45 46static const UChar ustrLangStatusDefacto[] = {0x64,0x65,0x5F,0x66,0x61,0x63,0x74,0x6F,0x5F,0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0}; //"de_facto_official" 47static const UChar ustrLangStatusOfficial[] = {0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0}; //"official" 48static const UChar ustrLangStatusRegional[] = {0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0x5F,0x72,0x65,0x67,0x69,0x6F,0x6E,0x61,0x6C,0}; //"official_regional" 49 50enum { 51 kLocalLangEntriesMax = 26, // enough for most regions to minimumFraction 0.001 except India 52 kLangEntriesFactor = 3 // if we have to allocate, multiply existing size by this 53}; 54 55U_CAPI int32_t U_EXPORT2 56ualoc_getLanguagesForRegion(const char *regionID, double minimumFraction, 57 UALanguageEntry *entries, int32_t entriesCapacity, 58 UErrorCode *err) 59{ 60 if (U_FAILURE(*err)) { 61 return 0; 62 } 63 if ( regionID == NULL || minimumFraction < 0.0 || minimumFraction > 1.0 || 64 ((entries==NULL)? entriesCapacity!=0: entriesCapacity<0) ) { 65 *err = U_ILLEGAL_ARGUMENT_ERROR; 66 return 0; 67 } 68 UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", err); 69 rb = ures_getByKey(rb, "territoryInfo", rb, err); 70 rb = ures_getByKey(rb, regionID, rb, err); 71 if (U_FAILURE(*err)) { 72 ures_close(rb); 73 return 0; 74 } 75 76 int32_t entryCount = 0; 77 UResourceBundle *langBund = NULL; 78 int32_t lbIdx, lbCount = ures_getSize(rb); 79 UALanguageEntry localLangEntries[kLocalLangEntriesMax]; 80 UALanguageEntry * langEntries = localLangEntries; 81 int32_t langEntriesMax = kLocalLangEntriesMax; 82 83 for (lbIdx = 0; lbIdx < lbCount; lbIdx++) { 84 langBund = ures_getByIndex(rb, lbIdx, langBund, err); 85 if (U_FAILURE(*err)) { 86 break; 87 } 88 const char * langCode = ures_getKey(langBund); 89 if (uprv_strcmp(langCode,"territoryF") == 0) { 90 continue; 91 } 92 if (strnlen(langCode, UALANGDATA_CODELEN+1) > UALANGDATA_CODELEN) { // no uprv_strnlen 93 continue; // a code we cannot handle 94 } 95 96 UErrorCode localErr = U_ZERO_ERROR; 97 double userFraction = 0.0; 98 UResourceBundle *itemBund = ures_getByKey(langBund, "populationShareF", NULL, &localErr); 99 if (U_SUCCESS(localErr)) { 100 int32_t intF = ures_getInt(itemBund, &localErr); 101 if (U_SUCCESS(localErr)) { 102 userFraction = doubleFromIntF(intF); 103 } 104 ures_close(itemBund); 105 } 106 if (userFraction < minimumFraction) { 107 continue; 108 } 109 if (entries != NULL) { 110 localErr = U_ZERO_ERROR; 111 UALanguageStatus langStatus = UALANGSTATUS_UNSPECIFIED; 112 int32_t ulen; 113 const UChar * ustrLangStatus = ures_getStringByKey(langBund, "officialStatus", &ulen, &localErr); 114 if (U_SUCCESS(localErr)) { 115 int32_t cmp = u_strcmp(ustrLangStatus, ustrLangStatusOfficial); 116 if (cmp == 0) { 117 langStatus = UALANGSTATUS_OFFICIAL; 118 } else if (cmp < 0 && u_strcmp(ustrLangStatus, ustrLangStatusDefacto) == 0) { 119 langStatus = UALANGSTATUS_DEFACTO_OFFICIAL; 120 } else if (u_strcmp(ustrLangStatus, ustrLangStatusRegional) == 0) { 121 langStatus = UALANGSTATUS_REGIONAL_OFFICIAL; 122 } 123 } 124 // Now we have all of the info for our next entry 125 if (entryCount >= langEntriesMax) { 126 int32_t newMax = langEntriesMax * kLangEntriesFactor; 127 if (langEntries == localLangEntries) { 128 // first allocation, copy from local buf 129 langEntries = (UALanguageEntry*)uprv_malloc(newMax*sizeof(UALanguageEntry)); 130 if (langEntries == NULL) { 131 *err = U_MEMORY_ALLOCATION_ERROR; 132 break; 133 } 134 uprv_memcpy(langEntries, localLangEntries, entryCount*sizeof(UALanguageEntry)); 135 } else { 136 langEntries = (UALanguageEntry*)uprv_realloc(langEntries, newMax*sizeof(UALanguageEntry)); 137 if (langEntries == NULL) { 138 *err = U_MEMORY_ALLOCATION_ERROR; 139 break; 140 } 141 } 142 langEntriesMax = newMax; 143 } 144 uprv_strcpy(langEntries[entryCount].languageCode, langCode); 145 langEntries[entryCount].userFraction = userFraction; 146 langEntries[entryCount].status = langStatus; 147 } 148 entryCount++; 149 } 150 ures_close(langBund); 151 ures_close(rb); 152 if (U_FAILURE(*err)) { 153 if (langEntries != localLangEntries) { 154 free(langEntries); 155 } 156 return 0; 157 } 158 if (entries != NULL) { 159 // sort langEntries, copy entries that fit to provided array 160 qsort(langEntries, entryCount, sizeof(UALanguageEntry), compareLangEntries); 161 if (entryCount > entriesCapacity) { 162 entryCount = entriesCapacity; 163 } 164 uprv_memcpy(entries, langEntries, entryCount*sizeof(UALanguageEntry)); 165 if (langEntries != localLangEntries) { 166 free(langEntries); 167 } 168 } 169 return entryCount; 170} 171 172 173static const char * forceParent[] = { 174 "zh", "zh_CN", 175 "zh_CN", "root", 176 "zh_Hant", "zh_TW", 177 "zh_TW", "root", 178 NULL 179}; 180 181U_CAPI int32_t U_EXPORT2 182ualoc_getAppleParent(const char* localeID, 183 char * parent, 184 int32_t parentCapacity, 185 UErrorCode* err) 186{ 187 UResourceBundle *rb; 188 int32_t len; 189 UErrorCode tempStatus; 190 char locbuf[ULOC_FULLNAME_CAPACITY+1]; 191 192 if (U_FAILURE(*err)) { 193 return 0; 194 } 195 if ( (parent==NULL)? parentCapacity!=0: parentCapacity<0 ) { 196 *err = U_ILLEGAL_ARGUMENT_ERROR; 197 return 0; 198 } 199 len = uloc_canonicalize(localeID, locbuf, ULOC_FULLNAME_CAPACITY, err); 200 if (U_FAILURE(*err)) { 201 return 0; 202 } 203 if (*err == U_STRING_NOT_TERMINATED_WARNING) { 204 locbuf[ULOC_FULLNAME_CAPACITY] = 0; 205 *err = U_ZERO_ERROR; 206 } 207 if (len >= 2 && uprv_strncmp(locbuf, "zh", 2) == 0) { 208 const char ** forceParentPtr = forceParent; 209 const char * testCurLoc; 210 while ( (testCurLoc = *forceParentPtr++) != NULL ) { 211 int cmp = uprv_strcmp(locbuf, testCurLoc); 212 if (cmp <= 0) { 213 if (cmp == 0) { 214 len = uprv_strlen(*forceParentPtr); 215 if (len < parentCapacity) { 216 uprv_strcpy(parent, *forceParentPtr); 217 } else { 218 *err = U_BUFFER_OVERFLOW_ERROR; 219 } 220 return len; 221 } 222 break; 223 } 224 forceParentPtr++; 225 } 226 } 227 tempStatus = U_ZERO_ERROR; 228 rb = ures_openDirect(NULL, locbuf, &tempStatus); 229 if (U_SUCCESS(tempStatus)) { 230 const char * actualLocale = ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &tempStatus); 231 if (U_SUCCESS(tempStatus) && uprv_strcmp(locbuf, actualLocale) != 0) { 232 // we have followed an alias 233 len = uprv_strlen(actualLocale); 234 if (len < parentCapacity) { 235 uprv_strcpy(parent, actualLocale); 236 } else { 237 *err = U_BUFFER_OVERFLOW_ERROR; 238 } 239 ures_close(rb); 240 return len; 241 } 242 tempStatus = U_ZERO_ERROR; 243 const UChar * parentUName = ures_getStringByKey(rb, "%%Parent", &len, &tempStatus); 244 if (U_SUCCESS(tempStatus) && tempStatus != U_USING_FALLBACK_WARNING) { 245 if (len < parentCapacity) { 246 u_UCharsToChars(parentUName, parent, len + 1); 247 } else { 248 *err = U_BUFFER_OVERFLOW_ERROR; 249 } 250 ures_close(rb); 251 return len; 252 } 253 ures_close(rb); 254 } 255 len = uloc_getParent(locbuf, parent, parentCapacity, err); 256 if (U_SUCCESS(*err) && len == 0) { 257 len = 4; 258 if (len < parentCapacity) { 259 uprv_strcpy(parent, "root"); 260 } else { 261 *err = U_BUFFER_OVERFLOW_ERROR; 262 } 263 } 264 return len; 265} 266 267