]>
Commit | Line | Data |
---|---|---|
57a6839d A |
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 | // | |
30 | static 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 | ||
36 | static 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 | ||
46 | static const UChar ustrLangStatusDefacto[] = {0x64,0x65,0x5F,0x66,0x61,0x63,0x74,0x6F,0x5F,0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0}; //"de_facto_official" | |
47 | static const UChar ustrLangStatusOfficial[] = {0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0}; //"official" | |
48 | static const UChar ustrLangStatusRegional[] = {0x6F,0x66,0x66,0x69,0x63,0x69,0x61,0x6C,0x5F,0x72,0x65,0x67,0x69,0x6F,0x6E,0x61,0x6C,0}; //"official_regional" | |
49 | ||
50 | enum { | |
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 | ||
55 | U_CAPI int32_t U_EXPORT2 | |
56 | ualoc_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 | ||
173 | static const char * forceParent[] = { | |
174 | "zh", "zh_CN", | |
175 | "zh_CN", "root", | |
176 | "zh_Hant", "zh_TW", | |
177 | "zh_TW", "root", | |
178 | NULL | |
179 | }; | |
180 | ||
181 | U_CAPI int32_t U_EXPORT2 | |
182 | ualoc_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 |