1 /*
2 **********************************************************************
3 * Copyright (C) 1997-2012, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
6 *
7 * File ULOC.CPP
8 *
9 * Modification History:
10 *
11 * Date Name Description
12 * 04/01/97 aliu Creation.
13 * 08/21/98 stephen JDK 1.2 sync
14 * 12/08/98 rtg New Locale implementation and C API
15 * 03/15/99 damiba overhaul.
16 * 04/06/99 stephen changed setDefault() to realloc and copy
17 * 06/14/99 stephen Changed calls to ures_open for new params
18 * 07/21/99 stephen Modified setDefault() to propagate to C++
19 * 05/14/04 alan 7 years later: refactored, cleaned up, fixed bugs,
20 * brought canonicalization code into line with spec
21 *****************************************************************************/
22
23 /*
24 POSIX's locale format, from putil.c: [no spaces]
25
26 ll [ _CC ] [ . MM ] [ @ VV]
27
28 l = lang, C = ctry, M = charmap, V = variant
29 */
30
31 #include "unicode/utypes.h"
32 #include "unicode/ustring.h"
33 #include "unicode/uloc.h"
34
35 #include "putilimp.h"
36 #include "ustr_imp.h"
37 #include "ulocimp.h"
38 #include "umutex.h"
39 #include "cstring.h"
40 #include "cmemory.h"
41 #include "ucln_cmn.h"
42 #include "locmap.h"
43 #include "uarrsort.h"
44 #include "uenumimp.h"
45 #include "uassert.h"
46
47 #include <stdio.h> /* for sprintf */
48
49 /* ### Declarations **************************************************/
50
51 /* Locale stuff from locid.cpp */
52 U_CFUNC void locale_set_default(const char *id);
53 U_CFUNC const char *locale_get_default(void);
54 U_CFUNC int32_t
55 locale_getKeywords(const char *localeID,
56 char prev,
57 char *keywords, int32_t keywordCapacity,
58 char *values, int32_t valuesCapacity, int32_t *valLen,
59 UBool valuesToo,
60 UErrorCode *status);
61
62 /* ### Data tables **************************************************/
63
64 /**
65 * Table of language codes, both 2- and 3-letter, with preference
66 * given to 2-letter codes where possible. Includes 3-letter codes
67 * that lack a 2-letter equivalent.
68 *
69 * This list must be in sorted order. This list is returned directly
70 * to the user by some API.
71 *
72 * This list must be kept in sync with LANGUAGES_3, with corresponding
73 * entries matched.
74 *
75 * This table should be terminated with a NULL entry, followed by a
76 * second list, and another NULL entry. The first list is visible to
77 * user code when this array is returned by API. The second list
78 * contains codes we support, but do not expose through user API.
79 *
80 * Notes
81 *
82 * Tables updated per http://lcweb.loc.gov/standards/iso639-2/ to
83 * include the revisions up to 2001/7/27 *CWB*
84 *
85 * The 3 character codes are the terminology codes like RFC 3066. This
86 * is compatible with prior ICU codes
87 *
88 * "in" "iw" "ji" "jw" & "sh" have been withdrawn but are still in the
89 * table but now at the end of the table because 3 character codes are
90 * duplicates. This avoids bad searches going from 3 to 2 character
91 * codes.
92 *
93 * The range qaa-qtz is reserved for local use
94 */
95 static const char * const LANGUAGES[] = {
96 "aa", "ab", "ace", "ach", "ada", "ady", "ae", "af", "afa",
97 "afh", "agq", "ain", "ak", "akk", "ale", "alg", "alt", "am", "an",
98 "ang", "anp", "apa",
99 "ar", "arc", "arn", "arp", "art", "arw", "as", "asa", "ast",
100 "ath", "aus", "av", "awa", "ay", "az", "ba", "bad",
101 "bai", "bal", "ban", "bas", "bat", "be", "bej",
102 "bem", "ber", "bez", "bg", "bh", "bho", "bi", "bik", "bin",
103 "bla", "bm", "bn", "bnt", "bo", "br", "bra", "brx", "bs",
104 "btk", "bua", "bug", "byn", "ca", "cad", "cai", "car", "cau",
105 "cch", "ce", "ceb", "cel", "cgg", "ch", "chb", "chg", "chk", "chm",
106 "chn", "cho", "chp", "chr", "chy", "cmc", "co", "cop",
107 "cpe", "cpf", "cpp", "cr", "crh", "crp", "cs", "csb", "cu", "cus",
108 "cv", "cy", "da", "dak", "dar", "dav", "day", "de", "del", "den",
109 "dgr", "din", "dje", "doi", "dra", "dsb", "dua", "dum", "dv", "dyo", "dyu",
110 "dz", "ebu", "ee", "efi", "egy", "eka", "el", "elx", "en",
111 "enm", "eo", "es", "et", "eu", "ewo", "fa",
112 "fan", "fat", "ff", "fi", "fil", "fiu", "fj", "fo", "fon",
113 "fr", "frm", "fro", "frr", "frs", "fur", "fy",
114 "ga", "gaa", "gan", "gay", "gba", "gd", "gem", "gez", "gil",
115 "gl", "gmh", "gn", "goh", "gon", "gor", "got", "grb",
116 "grc", "gsw", "gu", "guz", "gv", "gwi",
117 "ha", "hai", "hak", "haw", "he", "hi", "hil", "him",
118 "hit", "hmn", "ho", "hr", "hsb", "hsn", "ht", "hu", "hup", "hy", "hz",
119 "ia", "iba", "id", "ie", "ig", "ii", "ijo", "ik",
120 "ilo", "inc", "ine", "inh", "io", "ira", "iro", "is", "it",
121 "iu", "ja", "jbo", "jgo", "jmc", "jpr", "jrb", "jv", "ka", "kaa", "kab",
122 "kac", "kaj", "kam", "kar", "kaw", "kbd", "kcg", "kde", "kea", "kfo", "kg", "kha", "khi",
123 "kho", "khq", "ki", "kj", "kk", "kl", "kln", "km", "kmb", "kn",
124 "ko", "kok", "kos", "kpe", "kr", "krc", "krl", "kro", "kru", "ks", "ksb", "ksf",
125 "ku", "kum", "kut", "kv", "kw", "ky", "la", "lad", "lag",
126 "lah", "lam", "lb", "lez", "lg", "li", "ln", "lo", "lol",
127 "loz", "lt", "lu", "lua", "lui", "lun", "luo", "lus", "luy",
128 "lv", "mad", "mag", "mai", "mak", "man", "map", "mas",
129 "mdf", "mdr", "men", "mer", "mfe", "mg", "mga", "mgh", "mgo", "mh", "mi", "mic", "min",
130 "mis", "mk", "mkh", "ml", "mn", "mnc", "mni", "mno",
131 "mo", "moh", "mos", "mr", "ms", "mt", "mua", "mul", "mun",
132 "mus", "mwl", "mwr", "my", "myn", "myv", "na", "nah", "nai", "nan", "nap", "naq",
133 "nb", "nd", "nds", "ne", "new", "ng", "nia", "nic",
134 "niu", "nl", "nmg", "nn", "no", "nog", "non", "nqo", "nr", "nso", "nub", "nus",
135 "nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi", "oc", "oj",
136 "om", "or", "os", "osa", "ota", "oto", "pa", "paa",
137 "pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn",
138 "pi", "pl", "pon", "pra", "pro", "ps", "pt", "qu",
139 "raj", "rap", "rar", "rm", "rn", "ro", "roa", "rof", "rom",
140 "ru", "rup", "rw", "rwk", "sa", "sad", "sah", "sai", "sal", "sam", "saq",
141 "sas", "sat", "sbp", "sc", "scn", "sco", "sd", "se", "seh", "sel", "sem", "ses",
142 "sg", "sga", "sgn", "shi", "shn", "si", "sid", "sio", "sit",
143 "sk", "sl", "sla", "sm", "sma", "smi", "smj", "smn",
144 "sms", "sn", "snk", "so", "sog", "son", "sq", "sr",
145 "srn", "srr", "ss", "ssa", "st", "su", "suk", "sus", "sux",
146 "sv", "sw", "swc", "syc", "syr", "ta", "tai", "te", "tem", "teo", "ter",
147 "tet", "tg", "th", "ti", "tig", "tiv", "tk", "tkl",
148 "tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr", "trv",
149 "ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw", "twq",
150 "ty", "tyv", "tzm", "udm", "ug", "uga", "uk", "umb", "und", "ur",
151 "uz", "vai", "ve", "vi", "vo", "vot", "vun", "wa", "wak",
152 "wal", "war", "was", "wen", "wo", "wuu", "xal", "xh", "xog", "yao", "yap", "yav",
153 "yi", "yo", "ypk", "yue", "za", "zap", "zbl", "zen", "zh", "znd",
154 "zu", "zun", "zxx", "zza",
155 NULL,
156 "in", "iw", "ji", "jw", "sh", /* obsolete language codes */
157 NULL
158 };
159 static const char* const DEPRECATED_LANGUAGES[]={
160 "in", "iw", "ji", "jw", NULL, NULL
161 };
162 static const char* const REPLACEMENT_LANGUAGES[]={
163 "id", "he", "yi", "jv", NULL, NULL
164 };
165
166 /**
167 * Table of 3-letter language codes.
168 *
169 * This is a lookup table used to convert 3-letter language codes to
170 * their 2-letter equivalent, where possible. It must be kept in sync
171 * with LANGUAGES. For all valid i, LANGUAGES[i] must refer to the
172 * same language as LANGUAGES_3[i]. The commented-out lines are
173 * copied from LANGUAGES to make eyeballing this baby easier.
174 *
175 * Where a 3-letter language code has no 2-letter equivalent, the
176 * 3-letter code occupies both LANGUAGES[i] and LANGUAGES_3[i].
177 *
178 * This table should be terminated with a NULL entry, followed by a
179 * second list, and another NULL entry. The two lists correspond to
180 * the two lists in LANGUAGES.
181 */
182 static const char * const LANGUAGES_3[] = {
183 /* "aa", "ab", "ace", "ach", "ada", "ady", "ae", "af", "afa", */
184 "aar", "abk", "ace", "ach", "ada", "ady", "ave", "afr", "afa",
185 /* "afh", "agq", "ain", "ak", "akk", "ale", "alg", "alt", "am", "an", "ang", "anp", "apa", */
186 "afh", "agq", "ain", "aka", "akk", "ale", "alg", "alt", "amh", "arg", "ang", "anp", "apa",
187 /* "ar", "arc", "arn", "arp", "art", "arw", "as", "asa", "ast", */
188 "ara", "arc", "arn", "arp", "art", "arw", "asm", "asa", "ast",
189 /* "ath", "aus", "av", "awa", "ay", "az", "ba", "bad", */
190 "ath", "aus", "ava", "awa", "aym", "aze", "bak", "bad",
191 /* "bai", "bal", "ban", "bas", "bat", "be", "bej", */
192 "bai", "bal", "ban", "bas", "bat", "bel", "bej",
193 /* "bem", "ber", "bez", "bg", "bh", "bho", "bi", "bik", "bin", */
194 "bem", "ber", "bez", "bul", "bih", "bho", "bis", "bik", "bin",
195 /* "bla", "bm", "bn", "bnt", "bo", "br", "bra", "brx", "bs", */
196 "bla", "bam", "ben", "bnt", "bod", "bre", "bra", "brx", "bos",
197 /* "btk", "bua", "bug", "byn", "ca", "cad", "cai", "car", "cau", */
198 "btk", "bua", "bug", "byn", "cat", "cad", "cai", "car", "cau",
199 /* "cch", "ce", "ceb", "cel", "cgg", "ch", "chb", "chg", "chk", "chm", */
200 "cch", "che", "ceb", "cel", "cgg", "cha", "chb", "chg", "chk", "chm",
201 /* "chn", "cho", "chp", "chr", "chy", "cmc", "co", "cop", */
202 "chn", "cho", "chp", "chr", "chy", "cmc", "cos", "cop",
203 /* "cpe", "cpf", "cpp", "cr", "crh", "crp", "cs", "csb", "cu", "cus", */
204 "cpe", "cpf", "cpp", "cre", "crh", "crp", "ces", "csb", "chu", "cus",
205 /* "cv", "cy", "da", "dak", "dar", "dav", "day", "de", "del", "den", */
206 "chv", "cym", "dan", "dak", "dar", "dav", "day", "deu", "del", "den",
207 /* "dgr", "din", "dje", "doi", "dra", "dsb", "dua", "dum", "dv", "dyo", "dyu", */
208 "dgr", "din", "dje", "doi", "dra", "dsb", "dua", "dum", "div", "dyo", "dyu",
209 /* "dz", "ebu", "ee", "efi", "egy", "eka", "el", "elx", "en", */
210 "dzo", "ebu", "ewe", "efi", "egy", "eka", "ell", "elx", "eng",
211 /* "enm", "eo", "es", "et", "eu", "ewo", "fa", */
212 "enm", "epo", "spa", "est", "eus", "ewo", "fas",
213 /* "fan", "fat", "ff", "fi", "fil", "fiu", "fj", "fo", "fon", */
214 "fan", "fat", "ful", "fin", "fil", "fiu", "fij", "fao", "fon",
215 /* "fr", "frm", "fro", "frr", "frs", "fur", "fy", "ga", "gaa", "gan", "gay", */
216 "fra", "frm", "fro", "frr", "frs", "fur", "fry", "gle", "gaa", "gan", "gay",
217 /* "gba", "gd", "gem", "gez", "gil", "gl", "gmh", "gn", */
218 "gba", "gla", "gem", "gez", "gil", "glg", "gmh", "grn",
219 /* "goh", "gon", "gor", "got", "grb", "grc", "gsw", "gu", "guz", "gv", */
220 "goh", "gon", "gor", "got", "grb", "grc", "gsw", "guj", "guz", "glv",
221 /* "gwi", "ha", "hai", "hak", "haw", "he", "hi", "hil", "him", */
222 "gwi", "hau", "hai", "hak", "haw", "heb", "hin", "hil", "him",
223 /* "hit", "hmn", "ho", "hr", "hsb", "hsn", "ht", "hu", "hup", "hy", "hz", */
224 "hit", "hmn", "hmo", "hrv", "hsb", "hsn", "hat", "hun", "hup", "hye", "her",
225 /* "ia", "iba", "id", "ie", "ig", "ii", "ijo", "ik", */
226 "ina", "iba", "ind", "ile", "ibo", "iii", "ijo", "ipk",
227 /* "ilo", "inc", "ine", "inh", "io", "ira", "iro", "is", "it", */
228 "ilo", "inc", "ine", "inh", "ido", "ira", "iro", "isl", "ita",
229 /* "iu", "ja", "jbo", "jgo", "jmc", "jpr", "jrb", "jv", "ka", "kaa", "kab", */
230 "iku", "jpn", "jbo", "jgo", "jmc", "jpr", "jrb", "jav", "kat", "kaa", "kab",
231 /* "kac", "kaj", "kam", "kar", "kaw", "kbd", "kcg", "kde", "kea", "kfo", "kg", "kha", "khi",*/
232 "kac", "kaj", "kam", "kar", "kaw", "kbd", "kcg", "kde", "kea", "kfo", "kg", "kha", "khi",
233 /* "kho", "khq", "ki", "kj", "kk", "kl", "kln", "km", "kmb", "kn", */
234 "kho", "khq", "kik", "kua", "kaz", "kal", "kln", "khm", "kmb", "kan",
235 /* "ko", "kok", "kos", "kpe", "kr", "krc", "krl", "kro", "kru", "ks", "ksb", "ksf", */
236 "kor", "kok", "kos", "kpe", "kau", "krc", "krl", "kro", "kru", "kas", "ksb", "ksf",
237 /* "ku", "kum", "kut", "kv", "kw", "ky", "la", "lad", "lag", */
238 "kur", "kum", "kut", "kom", "cor", "kir", "lat", "lad", "lag",
239 /* "lah", "lam", "lb", "lez", "lg", "li", "ln", "lo", "lol", */
240 "lah", "lam", "ltz", "lez", "lug", "lim", "lin", "lao", "lol",
241 /* "loz", "lt", "lu", "lua", "lui", "lun", "luo", "lus", "luy", */
242 "loz", "lit", "lub", "lua", "lui", "lun", "luo", "lus", "luy",
243 /* "lv", "mad", "mag", "mai", "mak", "man", "map", "mas", */
244 "lav", "mad", "mag", "mai", "mak", "man", "map", "mas",
245 /* "mdf", "mdr", "men", "mer", "mfe", "mg", "mga", "mgh", "mgo", "mh", "mi", "mic", "min", */
246 "mdf", "mdr", "men", "mer", "mfe", "mlg", "mga", "mgh", "mgo", "mah", "mri", "mic", "min",
247 /* "mis", "mk", "mkh", "ml", "mn", "mnc", "mni", "mno", */
248 "mis", "mkd", "mkh", "mal", "mon", "mnc", "mni", "mno",
249 /* "mo", "moh", "mos", "mr", "ms", "mt", "mua", "mul", "mun", */
250 "mol", "moh", "mos", "mar", "msa", "mlt", "mua", "mul", "mun",
251 /* "mus", "mwl", "mwr", "my", "myn", "myv", "na", "nah", "nai", "nan", "nap", "naq", */
252 "mus", "mwl", "mwr", "mya", "myn", "myv", "nau", "nah", "nai", "nan", "nap", "naq",
253 /* "nb", "nd", "nds", "ne", "new", "ng", "nia", "nic", */
254 "nob", "nde", "nds", "nep", "new", "ndo", "nia", "nic",
255 /* "niu", "nl", "nmg", "nn", "no", "nog", "non", "nqo", "nr", "nso", "nub", "nus", */
256 "niu", "nld", "nmg", "nno", "nor", "nog", "non", "nqo", "nbl", "nso", "nub", "nus",
257 /* "nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi", "oc", "oj", */
258 "nav", "nwc", "nya", "nym", "nyn", "nyo", "nzi", "oci", "oji",
259 /* "om", "or", "os", "osa", "ota", "oto", "pa", "paa", */
260 "orm", "ori", "oss", "osa", "ota", "oto", "pan", "paa",
261 /* "pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn", */
262 "pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn",
263 /* "pi", "pl", "pon", "pra", "pro", "ps", "pt", "qu", */
264 "pli", "pol", "pon", "pra", "pro", "pus", "por", "que",
265 /* "raj", "rap", "rar", "rm", "rn", "ro", "roa", "rof", "rom", */
266 "raj", "rap", "rar", "roh", "run", "ron", "roa", "rof", "rom",
267 /* "ru", "rup", "rw", "rwk", "sa", "sad", "sah", "sai", "sal", "sam", "saq", */
268 "rus", "rup", "kin", "rwk", "san", "sad", "sah", "sai", "sal", "sam", "saq",
269 /* "sas", "sat", "sbp", "sc", "scn", "sco", "sd", "se", "seh", "sel", "sem", "ses", */
270 "sas", "sat", "sbp", "srd", "scn", "sco", "snd", "sme", "seh", "sel", "sem", "ses",
271 /* "sg", "sga", "sgn", "shi", "shn", "si", "sid", "sio", "sit", */
272 "sag", "sga", "sgn", "shi", "shn", "sin", "sid", "sio", "sit",
273 /* "sk", "sl", "sla", "sm", "sma", "smi", "smj", "smn", */
274 "slk", "slv", "sla", "smo", "sma", "smi", "smj", "smn",
275 /* "sms", "sn", "snk", "so", "sog", "son", "sq", "sr", */
276 "sms", "sna", "snk", "som", "sog", "son", "sqi", "srp",
277 /* "srn", "srr", "ss", "ssa", "st", "su", "suk", "sus", "sux", */
278 "srn", "srr", "ssw", "ssa", "sot", "sun", "suk", "sus", "sux",
279 /* "sv", "sw", "swc", "syc", "syr", "ta", "tai", "te", "tem", "teo", "ter", */
280 "swe", "swa", "swc", "syc", "syr", "tam", "tai", "tel", "tem", "teo", "ter",
281 /* "tet", "tg", "th", "ti", "tig", "tiv", "tk", "tkl", */
282 "tet", "tgk", "tha", "tir", "tig", "tiv", "tuk", "tkl",
283 /* "tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr", "trv", */
284 "tgl", "tlh", "tli", "tmh", "tsn", "ton", "tog", "tpi", "tur", "trv",
285 /* "ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw", "twq" */
286 "tso", "tsi", "tat", "tum", "tup", "tut", "tvl", "twi", "twq",
287 /* "ty", "tyv", "tzm", "udm", "ug", "uga", "uk", "umb", "und", "ur", */
288 "tah", "tyv", "tzm", "udm", "uig", "uga", "ukr", "umb", "und", "urd",
289 /* "uz", "vai", "ve", "vi", "vo", "vot", "vun", "wa", "wak", */
290 "uzb", "vai", "ven", "vie", "vol", "vot", "vun", "wln", "wak",
291 /* "wal", "war", "was", "wen", "wo", "wuu", "xal", "xh", "xog", "yao", "yap", "yav", */
292 "wal", "war", "was", "wen", "wol", "wuu", "xal", "xho", "xog", "yao", "yap", "yav",
293 /* "yi", "yo", "ypk", "yue", "za", "zap", "zbl", "zen", "zh", "znd", */
294 "yid", "yor", "ypk", "yue", "zha", "zap", "zbl", "zen", "zho", "znd",
295 /* "zu", "zun", "zxx", "zza", */
296 "zul", "zun", "zxx", "zza",
297 NULL,
298 /* "in", "iw", "ji", "jw", "sh", */
299 "ind", "heb", "yid", "jaw", "srp",
300 NULL
301 };
302
303 /**
304 * Table of 2-letter country codes.
305 *
306 * This list must be in sorted order. This list is returned directly
307 * to the user by some API.
308 *
309 * This list must be kept in sync with COUNTRIES_3, with corresponding
310 * entries matched.
311 *
312 * This table should be terminated with a NULL entry, followed by a
313 * second list, and another NULL entry. The first list is visible to
314 * user code when this array is returned by API. The second list
315 * contains codes we support, but do not expose through user API.
316 *
317 * Notes:
318 *
319 * ZR(ZAR) is now CD(COD) and FX(FXX) is PS(PSE) as per
320 * http://www.evertype.com/standards/iso3166/iso3166-1-en.html added
321 * new codes keeping the old ones for compatibility updated to include
322 * 1999/12/03 revisions *CWB*
323 *
324 * RO(ROM) is now RO(ROU) according to
325 * http://www.iso.org/iso/en/prods-services/iso3166ma/03updates-on-iso-3166/nlv3e-rou.html
326 */
327 static const char * const COUNTRIES[] = {
328 "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN",
329 "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ",
330 "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI",
331 "BJ", "BL", "BM", "BN", "BO", "BR", "BS", "BT", "BV",
332 "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG",
333 "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR",
334 "CU", "CV", "CX", "CY", "CZ", "DE", "DJ", "DK",
335 "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER",
336 "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR",
337 "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL",
338 "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU",
339 "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU",
340 "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS",
341 "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI",
342 "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA",
343 "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU",
344 "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK",
345 "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS",
346 "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA",
347 "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP",
348 "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG",
349 "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT",
350 "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA",
351 "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ",
352 "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV",
353 "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ",
354 "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV",
355 "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ",
356 "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF",
357 "WS", "YE", "YT", "ZA", "ZM", "ZW",
358 NULL,
359 "FX", "CS", "RO", "TP", "YU", "ZR", /* obsolete country codes */
360 NULL
361 };
362
363 static const char* const DEPRECATED_COUNTRIES[] ={
364 "BU", "CS", "DY", "FX", "HV", "NH", "RH", "TP", "YU", "ZR", NULL, NULL /* deprecated country list */
365 };
366 static const char* const REPLACEMENT_COUNTRIES[] = {
367 /* "BU", "CS", "DY", "FX", "HV", "NH", "RH", "TP", "YU", "ZR" */
368 "MM", "RS", "BJ", "FR", "BF", "VU", "ZW", "TL", "RS", "CD", NULL, NULL /* replacement country codes */
369 };
370
371 /**
372 * Table of 3-letter country codes.
373 *
374 * This is a lookup table used to convert 3-letter country codes to
375 * their 2-letter equivalent. It must be kept in sync with COUNTRIES.
376 * For all valid i, COUNTRIES[i] must refer to the same country as
377 * COUNTRIES_3[i]. The commented-out lines are copied from COUNTRIES
378 * to make eyeballing this baby easier.
379 *
380 * This table should be terminated with a NULL entry, followed by a
381 * second list, and another NULL entry. The two lists correspond to
382 * the two lists in COUNTRIES.
383 */
384 static const char * const COUNTRIES_3[] = {
385 /* "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", */
386 "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM", "ANT",
387 /* "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", */
388 "AGO", "ATA", "ARG", "ASM", "AUT", "AUS", "ABW", "ALA", "AZE",
389 /* "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", */
390 "BIH", "BRB", "BGD", "BEL", "BFA", "BGR", "BHR", "BDI",
391 /* "BJ", "BL", "BM", "BN", "BO", "BR", "BS", "BT", "BV", */
392 "BEN", "BLM", "BMU", "BRN", "BOL", "BRA", "BHS", "BTN", "BVT",
393 /* "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", */
394 "BWA", "BLR", "BLZ", "CAN", "CCK", "COD", "CAF", "COG",
395 /* "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", */
396 "CHE", "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CRI",
397 /* "CU", "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", */
398 "CUB", "CPV", "CXR", "CYP", "CZE", "DEU", "DJI", "DNK",
399 /* "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", */
400 "DMA", "DOM", "DZA", "ECU", "EST", "EGY", "ESH", "ERI",
401 /* "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", */
402 "ESP", "ETH", "FIN", "FJI", "FLK", "FSM", "FRO", "FRA",
403 /* "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", */
404 "GAB", "GBR", "GRD", "GEO", "GUF", "GGY", "GHA", "GIB", "GRL",
405 /* "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", */
406 "GMB", "GIN", "GLP", "GNQ", "GRC", "SGS", "GTM", "GUM",
407 /* "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", */
408 "GNB", "GUY", "HKG", "HMD", "HND", "HRV", "HTI", "HUN",
409 /* "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS" */
410 "IDN", "IRL", "ISR", "IMN", "IND", "IOT", "IRQ", "IRN", "ISL",
411 /* "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", */
412 "ITA", "JEY", "JAM", "JOR", "JPN", "KEN", "KGZ", "KHM", "KIR",
413 /* "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", */
414 "COM", "KNA", "PRK", "KOR", "KWT", "CYM", "KAZ", "LAO",
415 /* "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", */
416 "LBN", "LCA", "LIE", "LKA", "LBR", "LSO", "LTU", "LUX",
417 /* "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", */
418 "LVA", "LBY", "MAR", "MCO", "MDA", "MNE", "MAF", "MDG", "MHL", "MKD",
419 /* "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", */
420 "MLI", "MMR", "MNG", "MAC", "MNP", "MTQ", "MRT", "MSR",
421 /* "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", */
422 "MLT", "MUS", "MDV", "MWI", "MEX", "MYS", "MOZ", "NAM",
423 /* "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", */
424 "NCL", "NER", "NFK", "NGA", "NIC", "NLD", "NOR", "NPL",
425 /* "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", */
426 "NRU", "NIU", "NZL", "OMN", "PAN", "PER", "PYF", "PNG",
427 /* "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", */
428 "PHL", "PAK", "POL", "SPM", "PCN", "PRI", "PSE", "PRT",
429 /* "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", */
430 "PLW", "PRY", "QAT", "REU", "ROU", "SRB", "RUS", "RWA", "SAU",
431 /* "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", */
432 "SLB", "SYC", "SDN", "SWE", "SGP", "SHN", "SVN", "SJM",
433 /* "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", */
434 "SVK", "SLE", "SMR", "SEN", "SOM", "SUR", "STP", "SLV",
435 /* "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", */
436 "SYR", "SWZ", "TCA", "TCD", "ATF", "TGO", "THA", "TJK",
437 /* "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", */
438 "TKL", "TLS", "TKM", "TUN", "TON", "TUR", "TTO", "TUV",
439 /* "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", */
440 "TWN", "TZA", "UKR", "UGA", "UMI", "USA", "URY", "UZB",
441 /* "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", */
442 "VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT", "WLF",
443 /* "WS", "YE", "YT", "ZA", "ZM", "ZW", */
444 "WSM", "YEM", "MYT", "ZAF", "ZMB", "ZWE",
445 NULL,
446 /* "FX", "CS", "RO", "TP", "YU", "ZR", */
447 "FXX", "SCG", "ROM", "TMP", "YUG", "ZAR",
448 NULL
449 };
450
451 typedef struct CanonicalizationMap {
452 const char *id; /* input ID */
453 const char *canonicalID; /* canonicalized output ID */
454 const char *keyword; /* keyword, or NULL if none */
455 const char *value; /* keyword value, or NULL if kw==NULL */
456 } CanonicalizationMap;
457
458 /**
459 * A map to canonicalize locale IDs. This handles a variety of
460 * different semantic kinds of transformations.
461 */
462 static const CanonicalizationMap CANONICALIZE_MAP[] = {
463 { "", "en_US_POSIX", NULL, NULL }, /* .NET name */
464 { "c", "en_US_POSIX", NULL, NULL }, /* POSIX name */
465 { "posix", "en_US_POSIX", NULL, NULL }, /* POSIX name (alias of C) */
466 { "art_LOJBAN", "jbo", NULL, NULL }, /* registered name */
467 { "az_AZ_CYRL", "az_Cyrl_AZ", NULL, NULL }, /* .NET name */
468 { "az_AZ_LATN", "az_Latn_AZ", NULL, NULL }, /* .NET name */
469 { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" },
470 { "de__PHONEBOOK", "de", "collation", "phonebook" }, /* Old ICU name */
471 { "de_AT_PREEURO", "de_AT", "currency", "ATS" },
472 { "de_DE_PREEURO", "de_DE", "currency", "DEM" },
473 { "de_LU_PREEURO", "de_LU", "currency", "LUF" },
474 { "el_GR_PREEURO", "el_GR", "currency", "GRD" },
475 { "en_BE_PREEURO", "en_BE", "currency", "BEF" },
476 { "en_IE_PREEURO", "en_IE", "currency", "IEP" },
477 { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
478 { "es_ES_PREEURO", "es_ES", "currency", "ESP" },
479 { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" },
480 { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" },
481 { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" },
482 { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" },
483 { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" },
484 { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" },
485 { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" },
486 { "hi__DIRECT", "hi", "collation", "direct" }, /* Old ICU name */
487 { "it_IT_PREEURO", "it_IT", "currency", "ITL" },
488 { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" }, /* Old ICU name */
489 { "nb_NO_NY", "nn_NO", NULL, NULL }, /* "markus said this was ok" :-) */
490 { "nl_BE_PREEURO", "nl_BE", "currency", "BEF" },
491 { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" },
492 { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" },
493 { "sr_SP_CYRL", "sr_Cyrl_RS", NULL, NULL }, /* .NET name */
494 { "sr_SP_LATN", "sr_Latn_RS", NULL, NULL }, /* .NET name */
495 { "sr_YU_CYRILLIC", "sr_Cyrl_RS", NULL, NULL }, /* Linux name */
496 { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
497 { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", NULL, NULL }, /* Linux name */
498 { "uz_UZ_CYRL", "uz_Cyrl_UZ", NULL, NULL }, /* .NET name */
499 { "uz_UZ_LATN", "uz_Latn_UZ", NULL, NULL }, /* .NET name */
500 { "zh_CHS", "zh_Hans", NULL, NULL }, /* .NET name */
501 { "zh_CHT", "zh_Hant", NULL, NULL }, /* .NET name */
502 { "zh_GAN", "gan", NULL, NULL }, /* registered name */
503 { "zh_GUOYU", "zh", NULL, NULL }, /* registered name */
504 { "zh_HAKKA", "hak", NULL, NULL }, /* registered name */
505 { "zh_MIN_NAN", "nan", NULL, NULL }, /* registered name */
506 { "zh_WUU", "wuu", NULL, NULL }, /* registered name */
507 { "zh_XIANG", "hsn", NULL, NULL }, /* registered name */
508 { "zh_YUE", "yue", NULL, NULL }, /* registered name */
509 };
510
511 typedef struct VariantMap {
512 const char *variant; /* input ID */
513 const char *keyword; /* keyword, or NULL if none */
514 const char *value; /* keyword value, or NULL if kw==NULL */
515 } VariantMap;
516
517 static const VariantMap VARIANT_MAP[] = {
518 { "EURO", "currency", "EUR" },
519 { "PINYIN", "collation", "pinyin" }, /* Solaris variant */
520 { "STROKE", "collation", "stroke" } /* Solaris variant */
521 };
522
523 /* ### BCP47 Conversion *******************************************/
524 /* Test if the locale id has BCP47 u extension and does not have '@' */
525 #define _hasBCP47Extension(id) (id && uprv_strstr(id, "@") == NULL && getShortestSubtagLength(localeID) == 1)
526 /* Converts the BCP47 id to Unicode id. Does nothing to id if conversion fails */
527 #define _ConvertBCP47(finalID, id, buffer, length,err) \
528 if (uloc_forLanguageTag(id, buffer, length, NULL, err) <= 0 || U_FAILURE(*err)) { \
529 finalID=id; \
530 } else { \
531 finalID=buffer; \
532 }
533 /* Gets the size of the shortest subtag in the given localeID. */
getShortestSubtagLength(const char * localeID)534 static int32_t getShortestSubtagLength(const char *localeID) {
535 int32_t localeIDLength = uprv_strlen(localeID);
536 int32_t length = localeIDLength;
537 int32_t tmpLength = 0;
538 int32_t i;
539 UBool reset = TRUE;
540
541 for (i = 0; i < localeIDLength; i++) {
542 if (localeID[i] != '_' && localeID[i] != '-') {
543 if (reset) {
544 tmpLength = 0;
545 reset = FALSE;
546 }
547 tmpLength++;
548 } else {
549 if (tmpLength != 0 && tmpLength < length) {
550 length = tmpLength;
551 }
552 reset = TRUE;
553 }
554 }
555
556 return length;
557 }
558
559 /* ### Keywords **************************************************/
560
561 #define ULOC_KEYWORD_BUFFER_LEN 25
562 #define ULOC_MAX_NO_KEYWORDS 25
563
564 U_CAPI const char * U_EXPORT2
locale_getKeywordsStart(const char * localeID)565 locale_getKeywordsStart(const char *localeID) {
566 const char *result = NULL;
567 if((result = uprv_strchr(localeID, '@')) != NULL) {
568 return result;
569 }
570 #if (U_CHARSET_FAMILY == U_EBCDIC_FAMILY)
571 else {
572 /* We do this because the @ sign is variant, and the @ sign used on one
573 EBCDIC machine won't be compiled the same way on other EBCDIC based
574 machines. */
575 static const uint8_t ebcdicSigns[] = { 0x7C, 0x44, 0x66, 0x80, 0xAC, 0xAE, 0xAF, 0xB5, 0xEC, 0xEF, 0x00 };
576 const uint8_t *charToFind = ebcdicSigns;
577 while(*charToFind) {
578 if((result = uprv_strchr(localeID, *charToFind)) != NULL) {
579 return result;
580 }
581 charToFind++;
582 }
583 }
584 #endif
585 return NULL;
586 }
587
588 /**
589 * @param buf buffer of size [ULOC_KEYWORD_BUFFER_LEN]
590 * @param keywordName incoming name to be canonicalized
591 * @param status return status (keyword too long)
592 * @return length of the keyword name
593 */
locale_canonKeywordName(char * buf,const char * keywordName,UErrorCode * status)594 static int32_t locale_canonKeywordName(char *buf, const char *keywordName, UErrorCode *status)
595 {
596 int32_t i;
597 int32_t keywordNameLen = (int32_t)uprv_strlen(keywordName);
598
599 if(keywordNameLen >= ULOC_KEYWORD_BUFFER_LEN) {
600 /* keyword name too long for internal buffer */
601 *status = U_INTERNAL_PROGRAM_ERROR;
602 return 0;
603 }
604
605 /* normalize the keyword name */
606 for(i = 0; i < keywordNameLen; i++) {
607 buf[i] = uprv_tolower(keywordName[i]);
608 }
609 buf[i] = 0;
610
611 return keywordNameLen;
612 }
613
614 typedef struct {
615 char keyword[ULOC_KEYWORD_BUFFER_LEN];
616 int32_t keywordLen;
617 const char *valueStart;
618 int32_t valueLen;
619 } KeywordStruct;
620
621 static int32_t U_CALLCONV
compareKeywordStructs(const void *,const void * left,const void * right)622 compareKeywordStructs(const void * /*context*/, const void *left, const void *right) {
623 const char* leftString = ((const KeywordStruct *)left)->keyword;
624 const char* rightString = ((const KeywordStruct *)right)->keyword;
625 return uprv_strcmp(leftString, rightString);
626 }
627
628 /**
629 * Both addKeyword and addValue must already be in canonical form.
630 * Either both addKeyword and addValue are NULL, or neither is NULL.
631 * If they are not NULL they must be zero terminated.
632 * If addKeyword is not NULL is must have length small enough to fit in KeywordStruct.keyword.
633 */
634 static int32_t
_getKeywords(const char * localeID,char prev,char * keywords,int32_t keywordCapacity,char * values,int32_t valuesCapacity,int32_t * valLen,UBool valuesToo,const char * addKeyword,const char * addValue,UErrorCode * status)635 _getKeywords(const char *localeID,
636 char prev,
637 char *keywords, int32_t keywordCapacity,
638 char *values, int32_t valuesCapacity, int32_t *valLen,
639 UBool valuesToo,
640 const char* addKeyword,
641 const char* addValue,
642 UErrorCode *status)
643 {
644 KeywordStruct keywordList[ULOC_MAX_NO_KEYWORDS];
645
646 int32_t maxKeywords = ULOC_MAX_NO_KEYWORDS;
647 int32_t numKeywords = 0;
648 const char* pos = localeID;
649 const char* equalSign = NULL;
650 const char* semicolon = NULL;
651 int32_t i = 0, j, n;
652 int32_t keywordsLen = 0;
653 int32_t valuesLen = 0;
654
655 if(prev == '@') { /* start of keyword definition */
656 /* we will grab pairs, trim spaces, lowercase keywords, sort and return */
657 do {
658 UBool duplicate = FALSE;
659 /* skip leading spaces */
660 while(*pos == ' ') {
661 pos++;
662 }
663 if (!*pos) { /* handle trailing "; " */
664 break;
665 }
666 if(numKeywords == maxKeywords) {
667 *status = U_INTERNAL_PROGRAM_ERROR;
668 return 0;
669 }
670 equalSign = uprv_strchr(pos, '=');
671 semicolon = uprv_strchr(pos, ';');
672 /* lack of '=' [foo@currency] is illegal */
673 /* ';' before '=' [foo@currency;collation=pinyin] is illegal */
674 if(!equalSign || (semicolon && semicolon<equalSign)) {
675 *status = U_INVALID_FORMAT_ERROR;
676 return 0;
677 }
678 /* need to normalize both keyword and keyword name */
679 if(equalSign - pos >= ULOC_KEYWORD_BUFFER_LEN) {
680 /* keyword name too long for internal buffer */
681 *status = U_INTERNAL_PROGRAM_ERROR;
682 return 0;
683 }
684 for(i = 0, n = 0; i < equalSign - pos; ++i) {
685 if (pos[i] != ' ') {
686 keywordList[numKeywords].keyword[n++] = uprv_tolower(pos[i]);
687 }
688 }
689 keywordList[numKeywords].keyword[n] = 0;
690 keywordList[numKeywords].keywordLen = n;
691 /* now grab the value part. First we skip the '=' */
692 equalSign++;
693 /* then we leading spaces */
694 while(*equalSign == ' ') {
695 equalSign++;
696 }
697 keywordList[numKeywords].valueStart = equalSign;
698
699 pos = semicolon;
700 i = 0;
701 if(pos) {
702 while(*(pos - i - 1) == ' ') {
703 i++;
704 }
705 keywordList[numKeywords].valueLen = (int32_t)(pos - equalSign - i);
706 pos++;
707 } else {
708 i = (int32_t)uprv_strlen(equalSign);
709 while(i && equalSign[i-1] == ' ') {
710 i--;
711 }
712 keywordList[numKeywords].valueLen = i;
713 }
714 /* If this is a duplicate keyword, then ignore it */
715 for (j=0; j<numKeywords; ++j) {
716 if (uprv_strcmp(keywordList[j].keyword, keywordList[numKeywords].keyword) == 0) {
717 duplicate = TRUE;
718 break;
719 }
720 }
721 if (!duplicate) {
722 ++numKeywords;
723 }
724 } while(pos);
725
726 /* Handle addKeyword/addValue. */
727 if (addKeyword != NULL) {
728 UBool duplicate = FALSE;
729 U_ASSERT(addValue != NULL);
730 /* Search for duplicate; if found, do nothing. Explicit keyword
731 overrides addKeyword. */
732 for (j=0; j<numKeywords; ++j) {
733 if (uprv_strcmp(keywordList[j].keyword, addKeyword) == 0) {
734 duplicate = TRUE;
735 break;
736 }
737 }
738 if (!duplicate) {
739 if (numKeywords == maxKeywords) {
740 *status = U_INTERNAL_PROGRAM_ERROR;
741 return 0;
742 }
743 uprv_strcpy(keywordList[numKeywords].keyword, addKeyword);
744 keywordList[numKeywords].keywordLen = (int32_t)uprv_strlen(addKeyword);
745 keywordList[numKeywords].valueStart = addValue;
746 keywordList[numKeywords].valueLen = (int32_t)uprv_strlen(addValue);
747 ++numKeywords;
748 }
749 } else {
750 U_ASSERT(addValue == NULL);
751 }
752
753 /* now we have a list of keywords */
754 /* we need to sort it */
755 uprv_sortArray(keywordList, numKeywords, sizeof(KeywordStruct), compareKeywordStructs, NULL, FALSE, status);
756
757 /* Now construct the keyword part */
758 for(i = 0; i < numKeywords; i++) {
759 if(keywordsLen + keywordList[i].keywordLen + 1< keywordCapacity) {
760 uprv_strcpy(keywords+keywordsLen, keywordList[i].keyword);
761 if(valuesToo) {
762 keywords[keywordsLen + keywordList[i].keywordLen] = '=';
763 } else {
764 keywords[keywordsLen + keywordList[i].keywordLen] = 0;
765 }
766 }
767 keywordsLen += keywordList[i].keywordLen + 1;
768 if(valuesToo) {
769 if(keywordsLen + keywordList[i].valueLen < keywordCapacity) {
770 uprv_strncpy(keywords+keywordsLen, keywordList[i].valueStart, keywordList[i].valueLen);
771 }
772 keywordsLen += keywordList[i].valueLen;
773
774 if(i < numKeywords - 1) {
775 if(keywordsLen < keywordCapacity) {
776 keywords[keywordsLen] = ';';
777 }
778 keywordsLen++;
779 }
780 }
781 if(values) {
782 if(valuesLen + keywordList[i].valueLen + 1< valuesCapacity) {
783 uprv_strcpy(values+valuesLen, keywordList[i].valueStart);
784 values[valuesLen + keywordList[i].valueLen] = 0;
785 }
786 valuesLen += keywordList[i].valueLen + 1;
787 }
788 }
789 if(values) {
790 values[valuesLen] = 0;
791 if(valLen) {
792 *valLen = valuesLen;
793 }
794 }
795 return u_terminateChars(keywords, keywordCapacity, keywordsLen, status);
796 } else {
797 return 0;
798 }
799 }
800
801 U_CFUNC int32_t
locale_getKeywords(const char * localeID,char prev,char * keywords,int32_t keywordCapacity,char * values,int32_t valuesCapacity,int32_t * valLen,UBool valuesToo,UErrorCode * status)802 locale_getKeywords(const char *localeID,
803 char prev,
804 char *keywords, int32_t keywordCapacity,
805 char *values, int32_t valuesCapacity, int32_t *valLen,
806 UBool valuesToo,
807 UErrorCode *status) {
808 return _getKeywords(localeID, prev, keywords, keywordCapacity,
809 values, valuesCapacity, valLen, valuesToo,
810 NULL, NULL, status);
811 }
812
813 U_CAPI int32_t U_EXPORT2
uloc_getKeywordValue(const char * localeID,const char * keywordName,char * buffer,int32_t bufferCapacity,UErrorCode * status)814 uloc_getKeywordValue(const char* localeID,
815 const char* keywordName,
816 char* buffer, int32_t bufferCapacity,
817 UErrorCode* status)
818 {
819 const char* startSearchHere = NULL;
820 const char* nextSeparator = NULL;
821 char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
822 char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
823 int32_t i = 0;
824 int32_t result = 0;
825
826 if(status && U_SUCCESS(*status) && localeID) {
827 char tempBuffer[ULOC_FULLNAME_CAPACITY];
828 const char* tmpLocaleID;
829
830 if (_hasBCP47Extension(localeID)) {
831 _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), status);
832 } else {
833 tmpLocaleID=localeID;
834 }
835
836 startSearchHere = uprv_strchr(tmpLocaleID, '@'); /* TODO: REVISIT: shouldn't this be locale_getKeywordsStart ? */
837 if(startSearchHere == NULL) {
838 /* no keywords, return at once */
839 return 0;
840 }
841
842 locale_canonKeywordName(keywordNameBuffer, keywordName, status);
843 if(U_FAILURE(*status)) {
844 return 0;
845 }
846
847 /* find the first keyword */
848 while(startSearchHere) {
849 startSearchHere++;
850 /* skip leading spaces (allowed?) */
851 while(*startSearchHere == ' ') {
852 startSearchHere++;
853 }
854 nextSeparator = uprv_strchr(startSearchHere, '=');
855 /* need to normalize both keyword and keyword name */
856 if(!nextSeparator) {
857 break;
858 }
859 if(nextSeparator - startSearchHere >= ULOC_KEYWORD_BUFFER_LEN) {
860 /* keyword name too long for internal buffer */
861 *status = U_INTERNAL_PROGRAM_ERROR;
862 return 0;
863 }
864 for(i = 0; i < nextSeparator - startSearchHere; i++) {
865 localeKeywordNameBuffer[i] = uprv_tolower(startSearchHere[i]);
866 }
867 /* trim trailing spaces */
868 while(startSearchHere[i-1] == ' ') {
869 i--;
870 U_ASSERT(i>=0);
871 }
872 localeKeywordNameBuffer[i] = 0;
873
874 startSearchHere = uprv_strchr(nextSeparator, ';');
875
876 if(uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer) == 0) {
877 nextSeparator++;
878 while(*nextSeparator == ' ') {
879 nextSeparator++;
880 }
881 /* we actually found the keyword. Copy the value */
882 if(startSearchHere && startSearchHere - nextSeparator < bufferCapacity) {
883 while(*(startSearchHere-1) == ' ') {
884 startSearchHere--;
885 }
886 uprv_strncpy(buffer, nextSeparator, startSearchHere - nextSeparator);
887 result = u_terminateChars(buffer, bufferCapacity, (int32_t)(startSearchHere - nextSeparator), status);
888 } else if(!startSearchHere && (int32_t)uprv_strlen(nextSeparator) < bufferCapacity) { /* last item in string */
889 i = (int32_t)uprv_strlen(nextSeparator);
890 while(nextSeparator[i - 1] == ' ') {
891 i--;
892 }
893 uprv_strncpy(buffer, nextSeparator, i);
894 result = u_terminateChars(buffer, bufferCapacity, i, status);
895 } else {
896 /* give a bigger buffer, please */
897 *status = U_BUFFER_OVERFLOW_ERROR;
898 if(startSearchHere) {
899 result = (int32_t)(startSearchHere - nextSeparator);
900 } else {
901 result = (int32_t)uprv_strlen(nextSeparator);
902 }
903 }
904 return result;
905 }
906 }
907 }
908 return 0;
909 }
910
911 U_CAPI int32_t U_EXPORT2
uloc_setKeywordValue(const char * keywordName,const char * keywordValue,char * buffer,int32_t bufferCapacity,UErrorCode * status)912 uloc_setKeywordValue(const char* keywordName,
913 const char* keywordValue,
914 char* buffer, int32_t bufferCapacity,
915 UErrorCode* status)
916 {
917 /* TODO: sorting. removal. */
918 int32_t keywordNameLen;
919 int32_t keywordValueLen;
920 int32_t bufLen;
921 int32_t needLen = 0;
922 int32_t foundValueLen;
923 int32_t keywordAtEnd = 0; /* is the keyword at the end of the string? */
924 char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
925 char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
926 int32_t i = 0;
927 int32_t rc;
928 char* nextSeparator = NULL;
929 char* nextEqualsign = NULL;
930 char* startSearchHere = NULL;
931 char* keywordStart = NULL;
932 char *insertHere = NULL;
933 if(U_FAILURE(*status)) {
934 return -1;
935 }
936 if(bufferCapacity>1) {
937 bufLen = (int32_t)uprv_strlen(buffer);
938 } else {
939 *status = U_ILLEGAL_ARGUMENT_ERROR;
940 return 0;
941 }
942 if(bufferCapacity<bufLen) {
943 /* The capacity is less than the length?! Is this NULL terminated? */
944 *status = U_ILLEGAL_ARGUMENT_ERROR;
945 return 0;
946 }
947 if(keywordValue && !*keywordValue) {
948 keywordValue = NULL;
949 }
950 if(keywordValue) {
951 keywordValueLen = (int32_t)uprv_strlen(keywordValue);
952 } else {
953 keywordValueLen = 0;
954 }
955 keywordNameLen = locale_canonKeywordName(keywordNameBuffer, keywordName, status);
956 if(U_FAILURE(*status)) {
957 return 0;
958 }
959 startSearchHere = (char*)locale_getKeywordsStart(buffer);
960 if(startSearchHere == NULL || (startSearchHere[1]==0)) {
961 if(!keywordValue) { /* no keywords = nothing to remove */
962 return bufLen;
963 }
964
965 needLen = bufLen+1+keywordNameLen+1+keywordValueLen;
966 if(startSearchHere) { /* had a single @ */
967 needLen--; /* already had the @ */
968 /* startSearchHere points at the @ */
969 } else {
970 startSearchHere=buffer+bufLen;
971 }
972 if(needLen >= bufferCapacity) {
973 *status = U_BUFFER_OVERFLOW_ERROR;
974 return needLen; /* no change */
975 }
976 *startSearchHere = '@';
977 startSearchHere++;
978 uprv_strcpy(startSearchHere, keywordNameBuffer);
979 startSearchHere += keywordNameLen;
980 *startSearchHere = '=';
981 startSearchHere++;
982 uprv_strcpy(startSearchHere, keywordValue);
983 startSearchHere+=keywordValueLen;
984 return needLen;
985 } /* end shortcut - no @ */
986
987 keywordStart = startSearchHere;
988 /* search for keyword */
989 while(keywordStart) {
990 keywordStart++;
991 /* skip leading spaces (allowed?) */
992 while(*keywordStart == ' ') {
993 keywordStart++;
994 }
995 nextEqualsign = uprv_strchr(keywordStart, '=');
996 /* need to normalize both keyword and keyword name */
997 if(!nextEqualsign) {
998 break;
999 }
1000 if(nextEqualsign - keywordStart >= ULOC_KEYWORD_BUFFER_LEN) {
1001 /* keyword name too long for internal buffer */
1002 *status = U_INTERNAL_PROGRAM_ERROR;
1003 return 0;
1004 }
1005 for(i = 0; i < nextEqualsign - keywordStart; i++) {
1006 localeKeywordNameBuffer[i] = uprv_tolower(keywordStart[i]);
1007 }
1008 /* trim trailing spaces */
1009 while(keywordStart[i-1] == ' ') {
1010 i--;
1011 }
1012 U_ASSERT(i>=0 && i<ULOC_KEYWORD_BUFFER_LEN);
1013 localeKeywordNameBuffer[i] = 0;
1014
1015 nextSeparator = uprv_strchr(nextEqualsign, ';');
1016 rc = uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer);
1017 if(rc == 0) {
1018 nextEqualsign++;
1019 while(*nextEqualsign == ' ') {
1020 nextEqualsign++;
1021 }
1022 /* we actually found the keyword. Change the value */
1023 if (nextSeparator) {
1024 keywordAtEnd = 0;
1025 foundValueLen = (int32_t)(nextSeparator - nextEqualsign);
1026 } else {
1027 keywordAtEnd = 1;
1028 foundValueLen = (int32_t)uprv_strlen(nextEqualsign);
1029 }
1030 if(keywordValue) { /* adding a value - not removing */
1031 if(foundValueLen == keywordValueLen) {
1032 uprv_strncpy(nextEqualsign, keywordValue, keywordValueLen);
1033 return bufLen; /* no change in size */
1034 } else if(foundValueLen > keywordValueLen) {
1035 int32_t delta = foundValueLen - keywordValueLen;
1036 if(nextSeparator) { /* RH side */
1037 uprv_memmove(nextSeparator - delta, nextSeparator, bufLen-(nextSeparator-buffer));
1038 }
1039 uprv_strncpy(nextEqualsign, keywordValue, keywordValueLen);
1040 bufLen -= delta;
1041 buffer[bufLen]=0;
1042 return bufLen;
1043 } else { /* FVL < KVL */
1044 int32_t delta = keywordValueLen - foundValueLen;
1045 if((bufLen+delta) >= bufferCapacity) {
1046 *status = U_BUFFER_OVERFLOW_ERROR;
1047 return bufLen+delta;
1048 }
1049 if(nextSeparator) { /* RH side */
1050 uprv_memmove(nextSeparator+delta,nextSeparator, bufLen-(nextSeparator-buffer));
1051 }
1052 uprv_strncpy(nextEqualsign, keywordValue, keywordValueLen);
1053 bufLen += delta;
1054 buffer[bufLen]=0;
1055 return bufLen;
1056 }
1057 } else { /* removing a keyword */
1058 if(keywordAtEnd) {
1059 /* zero out the ';' or '@' just before startSearchhere */
1060 keywordStart[-1] = 0;
1061 return (int32_t)((keywordStart-buffer)-1); /* (string length without keyword) minus separator */
1062 } else {
1063 uprv_memmove(keywordStart, nextSeparator+1, bufLen-((nextSeparator+1)-buffer));
1064 keywordStart[bufLen-((nextSeparator+1)-buffer)]=0;
1065 return (int32_t)(bufLen-((nextSeparator+1)-keywordStart));
1066 }
1067 }
1068 } else if(rc<0){ /* end match keyword */
1069 /* could insert at this location. */
1070 insertHere = keywordStart;
1071 }
1072 keywordStart = nextSeparator;
1073 } /* end loop searching */
1074
1075 if(!keywordValue) {
1076 return bufLen; /* removal of non-extant keyword - no change */
1077 }
1078
1079 /* we know there is at least one keyword. */
1080 needLen = bufLen+1+keywordNameLen+1+keywordValueLen;
1081 if(needLen >= bufferCapacity) {
1082 *status = U_BUFFER_OVERFLOW_ERROR;
1083 return needLen; /* no change */
1084 }
1085
1086 if(insertHere) {
1087 uprv_memmove(insertHere+(1+keywordNameLen+1+keywordValueLen), insertHere, bufLen-(insertHere-buffer));
1088 keywordStart = insertHere;
1089 } else {
1090 keywordStart = buffer+bufLen;
1091 *keywordStart = ';';
1092 keywordStart++;
1093 }
1094 uprv_strncpy(keywordStart, keywordNameBuffer, keywordNameLen);
1095 keywordStart += keywordNameLen;
1096 *keywordStart = '=';
1097 keywordStart++;
1098 uprv_strncpy(keywordStart, keywordValue, keywordValueLen); /* terminates. */
1099 keywordStart+=keywordValueLen;
1100 if(insertHere) {
1101 *keywordStart = ';';
1102 keywordStart++;
1103 }
1104 buffer[needLen]=0;
1105 return needLen;
1106 }
1107
1108 /* ### ID parsing implementation **************************************************/
1109
1110 #define _isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I'))
1111
1112 /*returns TRUE if one of the special prefixes is here (s=string)
1113 'x-' or 'i-' */
1114 #define _isIDPrefix(s) (_isPrefixLetter(s[0])&&_isIDSeparator(s[1]))
1115
1116 /* Dot terminates it because of POSIX form where dot precedes the codepage
1117 * except for variant
1118 */
1119 #define _isTerminator(a) ((a==0)||(a=='.')||(a=='@'))
1120
_strnchr(const char * str,int32_t len,char c)1121 static char* _strnchr(const char* str, int32_t len, char c) {
1122 U_ASSERT(str != 0 && len >= 0);
1123 while (len-- != 0) {
1124 char d = *str;
1125 if (d == c) {
1126 return (char*) str;
1127 } else if (d == 0) {
1128 break;
1129 }
1130 ++str;
1131 }
1132 return NULL;
1133 }
1134
1135 /**
1136 * Lookup 'key' in the array 'list'. The array 'list' should contain
1137 * a NULL entry, followed by more entries, and a second NULL entry.
1138 *
1139 * The 'list' param should be LANGUAGES, LANGUAGES_3, COUNTRIES, or
1140 * COUNTRIES_3.
1141 */
_findIndex(const char * const * list,const char * key)1142 static int16_t _findIndex(const char* const* list, const char* key)
1143 {
1144 const char* const* anchor = list;
1145 int32_t pass = 0;
1146
1147 /* Make two passes through two NULL-terminated arrays at 'list' */
1148 while (pass++ < 2) {
1149 while (*list) {
1150 if (uprv_strcmp(key, *list) == 0) {
1151 return (int16_t)(list - anchor);
1152 }
1153 list++;
1154 }
1155 ++list; /* skip final NULL *CWB*/
1156 }
1157 return -1;
1158 }
1159
1160 /* count the length of src while copying it to dest; return strlen(src) */
1161 static inline int32_t
_copyCount(char * dest,int32_t destCapacity,const char * src)1162 _copyCount(char *dest, int32_t destCapacity, const char *src) {
1163 const char *anchor;
1164 char c;
1165
1166 anchor=src;
1167 for(;;) {
1168 if((c=*src)==0) {
1169 return (int32_t)(src-anchor);
1170 }
1171 if(destCapacity<=0) {
1172 return (int32_t)((src-anchor)+uprv_strlen(src));
1173 }
1174 ++src;
1175 *dest++=c;
1176 --destCapacity;
1177 }
1178 }
1179
1180 U_CFUNC const char*
uloc_getCurrentCountryID(const char * oldID)1181 uloc_getCurrentCountryID(const char* oldID){
1182 int32_t offset = _findIndex(DEPRECATED_COUNTRIES, oldID);
1183 if (offset >= 0) {
1184 return REPLACEMENT_COUNTRIES[offset];
1185 }
1186 return oldID;
1187 }
1188 U_CFUNC const char*
uloc_getCurrentLanguageID(const char * oldID)1189 uloc_getCurrentLanguageID(const char* oldID){
1190 int32_t offset = _findIndex(DEPRECATED_LANGUAGES, oldID);
1191 if (offset >= 0) {
1192 return REPLACEMENT_LANGUAGES[offset];
1193 }
1194 return oldID;
1195 }
1196 /*
1197 * the internal functions _getLanguage(), _getCountry(), _getVariant()
1198 * avoid duplicating code to handle the earlier locale ID pieces
1199 * in the functions for the later ones by
1200 * setting the *pEnd pointer to where they stopped parsing
1201 *
1202 * TODO try to use this in Locale
1203 */
1204 U_CFUNC int32_t
ulocimp_getLanguage(const char * localeID,char * language,int32_t languageCapacity,const char ** pEnd)1205 ulocimp_getLanguage(const char *localeID,
1206 char *language, int32_t languageCapacity,
1207 const char **pEnd) {
1208 int32_t i=0;
1209 int32_t offset;
1210 char lang[4]={ 0, 0, 0, 0 }; /* temporary buffer to hold language code for searching */
1211
1212 /* if it starts with i- or x- then copy that prefix */
1213 if(_isIDPrefix(localeID)) {
1214 if(i<languageCapacity) {
1215 language[i]=(char)uprv_tolower(*localeID);
1216 }
1217 if(i<languageCapacity) {
1218 language[i+1]='-';
1219 }
1220 i+=2;
1221 localeID+=2;
1222 }
1223
1224 /* copy the language as far as possible and count its length */
1225 while(!_isTerminator(*localeID) && !_isIDSeparator(*localeID)) {
1226 if(i<languageCapacity) {
1227 language[i]=(char)uprv_tolower(*localeID);
1228 }
1229 if(i<3) {
1230 U_ASSERT(i>=0);
1231 lang[i]=(char)uprv_tolower(*localeID);
1232 }
1233 i++;
1234 localeID++;
1235 }
1236
1237 if(i==3) {
1238 /* convert 3 character code to 2 character code if possible *CWB*/
1239 offset=_findIndex(LANGUAGES_3, lang);
1240 if(offset>=0) {
1241 i=_copyCount(language, languageCapacity, LANGUAGES[offset]);
1242 }
1243 }
1244
1245 if(pEnd!=NULL) {
1246 *pEnd=localeID;
1247 }
1248 return i;
1249 }
1250
1251 U_CFUNC int32_t
ulocimp_getScript(const char * localeID,char * script,int32_t scriptCapacity,const char ** pEnd)1252 ulocimp_getScript(const char *localeID,
1253 char *script, int32_t scriptCapacity,
1254 const char **pEnd)
1255 {
1256 int32_t idLen = 0;
1257
1258 if (pEnd != NULL) {
1259 *pEnd = localeID;
1260 }
1261
1262 /* copy the second item as far as possible and count its length */
1263 while(!_isTerminator(localeID[idLen]) && !_isIDSeparator(localeID[idLen])
1264 && uprv_isASCIILetter(localeID[idLen])) {
1265 idLen++;
1266 }
1267
1268 /* If it's exactly 4 characters long, then it's a script and not a country. */
1269 if (idLen == 4) {
1270 int32_t i;
1271 if (pEnd != NULL) {
1272 *pEnd = localeID+idLen;
1273 }
1274 if(idLen > scriptCapacity) {
1275 idLen = scriptCapacity;
1276 }
1277 if (idLen >= 1) {
1278 script[0]=(char)uprv_toupper(*(localeID++));
1279 }
1280 for (i = 1; i < idLen; i++) {
1281 script[i]=(char)uprv_tolower(*(localeID++));
1282 }
1283 }
1284 else {
1285 idLen = 0;
1286 }
1287 return idLen;
1288 }
1289
1290 U_CFUNC int32_t
ulocimp_getCountry(const char * localeID,char * country,int32_t countryCapacity,const char ** pEnd)1291 ulocimp_getCountry(const char *localeID,
1292 char *country, int32_t countryCapacity,
1293 const char **pEnd)
1294 {
1295 int32_t idLen=0;
1296 char cnty[ULOC_COUNTRY_CAPACITY]={ 0, 0, 0, 0 };
1297 int32_t offset;
1298
1299 /* copy the country as far as possible and count its length */
1300 while(!_isTerminator(localeID[idLen]) && !_isIDSeparator(localeID[idLen])) {
1301 if(idLen<(ULOC_COUNTRY_CAPACITY-1)) { /*CWB*/
1302 cnty[idLen]=(char)uprv_toupper(localeID[idLen]);
1303 }
1304 idLen++;
1305 }
1306
1307 /* the country should be either length 2 or 3 */
1308 if (idLen == 2 || idLen == 3) {
1309 UBool gotCountry = FALSE;
1310 /* convert 3 character code to 2 character code if possible *CWB*/
1311 if(idLen==3) {
1312 offset=_findIndex(COUNTRIES_3, cnty);
1313 if(offset>=0) {
1314 idLen=_copyCount(country, countryCapacity, COUNTRIES[offset]);
1315 gotCountry = TRUE;
1316 }
1317 }
1318 if (!gotCountry) {
1319 int32_t i = 0;
1320 for (i = 0; i < idLen; i++) {
1321 if (i < countryCapacity) {
1322 country[i]=(char)uprv_toupper(localeID[i]);
1323 }
1324 }
1325 }
1326 localeID+=idLen;
1327 } else {
1328 idLen = 0;
1329 }
1330
1331 if(pEnd!=NULL) {
1332 *pEnd=localeID;
1333 }
1334
1335 return idLen;
1336 }
1337
1338 /**
1339 * @param needSeparator if true, then add leading '_' if any variants
1340 * are added to 'variant'
1341 */
1342 static int32_t
_getVariantEx(const char * localeID,char prev,char * variant,int32_t variantCapacity,UBool needSeparator)1343 _getVariantEx(const char *localeID,
1344 char prev,
1345 char *variant, int32_t variantCapacity,
1346 UBool needSeparator) {
1347 int32_t i=0;
1348
1349 /* get one or more variant tags and separate them with '_' */
1350 if(_isIDSeparator(prev)) {
1351 /* get a variant string after a '-' or '_' */
1352 while(!_isTerminator(*localeID)) {
1353 if (needSeparator) {
1354 if (i<variantCapacity) {
1355 variant[i] = '_';
1356 }
1357 ++i;
1358 needSeparator = FALSE;
1359 }
1360 if(i<variantCapacity) {
1361 variant[i]=(char)uprv_toupper(*localeID);
1362 if(variant[i]=='-') {
1363 variant[i]='_';
1364 }
1365 }
1366 i++;
1367 localeID++;
1368 }
1369 }
1370
1371 /* if there is no variant tag after a '-' or '_' then look for '@' */
1372 if(i==0) {
1373 if(prev=='@') {
1374 /* keep localeID */
1375 } else if((localeID=locale_getKeywordsStart(localeID))!=NULL) {
1376 ++localeID; /* point after the '@' */
1377 } else {
1378 return 0;
1379 }
1380 while(!_isTerminator(*localeID)) {
1381 if (needSeparator) {
1382 if (i<variantCapacity) {
1383 variant[i] = '_';
1384 }
1385 ++i;
1386 needSeparator = FALSE;
1387 }
1388 if(i<variantCapacity) {
1389 variant[i]=(char)uprv_toupper(*localeID);
1390 if(variant[i]=='-' || variant[i]==',') {
1391 variant[i]='_';
1392 }
1393 }
1394 i++;
1395 localeID++;
1396 }
1397 }
1398
1399 return i;
1400 }
1401
1402 static int32_t
_getVariant(const char * localeID,char prev,char * variant,int32_t variantCapacity)1403 _getVariant(const char *localeID,
1404 char prev,
1405 char *variant, int32_t variantCapacity) {
1406 return _getVariantEx(localeID, prev, variant, variantCapacity, FALSE);
1407 }
1408
1409 /**
1410 * Delete ALL instances of a variant from the given list of one or
1411 * more variants. Example: "FOO_EURO_BAR_EURO" => "FOO_BAR".
1412 * @param variants the source string of one or more variants,
1413 * separated by '_'. This will be MODIFIED IN PLACE. Not zero
1414 * terminated; if it is, trailing zero will NOT be maintained.
1415 * @param variantsLen length of variants
1416 * @param toDelete variant to delete, without separators, e.g. "EURO"
1417 * or "PREEURO"; not zero terminated
1418 * @param toDeleteLen length of toDelete
1419 * @return number of characters deleted from variants
1420 */
1421 static int32_t
_deleteVariant(char * variants,int32_t variantsLen,const char * toDelete,int32_t toDeleteLen)1422 _deleteVariant(char* variants, int32_t variantsLen,
1423 const char* toDelete, int32_t toDeleteLen)
1424 {
1425 int32_t delta = 0; /* number of chars deleted */
1426 for (;;) {
1427 UBool flag = FALSE;
1428 if (variantsLen < toDeleteLen) {
1429 return delta;
1430 }
1431 if (uprv_strncmp(variants, toDelete, toDeleteLen) == 0 &&
1432 (variantsLen == toDeleteLen ||
1433 (flag=(variants[toDeleteLen] == '_'))))
1434 {
1435 int32_t d = toDeleteLen + (flag?1:0);
1436 variantsLen -= d;
1437 delta += d;
1438 if (variantsLen > 0) {
1439 uprv_memmove(variants, variants+d, variantsLen);
1440 }
1441 } else {
1442 char* p = _strnchr(variants, variantsLen, '_');
1443 if (p == NULL) {
1444 return delta;
1445 }
1446 ++p;
1447 variantsLen -= (int32_t)(p - variants);
1448 variants = p;
1449 }
1450 }
1451 }
1452
1453 /* Keyword enumeration */
1454
1455 typedef struct UKeywordsContext {
1456 char* keywords;
1457 char* current;
1458 } UKeywordsContext;
1459
1460 static void U_CALLCONV
uloc_kw_closeKeywords(UEnumeration * enumerator)1461 uloc_kw_closeKeywords(UEnumeration *enumerator) {
1462 uprv_free(((UKeywordsContext *)enumerator->context)->keywords);
1463 uprv_free(enumerator->context);
1464 uprv_free(enumerator);
1465 }
1466
1467 static int32_t U_CALLCONV
uloc_kw_countKeywords(UEnumeration * en,UErrorCode *)1468 uloc_kw_countKeywords(UEnumeration *en, UErrorCode * /*status*/) {
1469 char *kw = ((UKeywordsContext *)en->context)->keywords;
1470 int32_t result = 0;
1471 while(*kw) {
1472 result++;
1473 kw += uprv_strlen(kw)+1;
1474 }
1475 return result;
1476 }
1477
1478 static const char* U_CALLCONV
uloc_kw_nextKeyword(UEnumeration * en,int32_t * resultLength,UErrorCode *)1479 uloc_kw_nextKeyword(UEnumeration* en,
1480 int32_t* resultLength,
1481 UErrorCode* /*status*/) {
1482 const char* result = ((UKeywordsContext *)en->context)->current;
1483 int32_t len = 0;
1484 if(*result) {
1485 len = (int32_t)uprv_strlen(((UKeywordsContext *)en->context)->current);
1486 ((UKeywordsContext *)en->context)->current += len+1;
1487 } else {
1488 result = NULL;
1489 }
1490 if (resultLength) {
1491 *resultLength = len;
1492 }
1493 return result;
1494 }
1495
1496 static void U_CALLCONV
uloc_kw_resetKeywords(UEnumeration * en,UErrorCode *)1497 uloc_kw_resetKeywords(UEnumeration* en,
1498 UErrorCode* /*status*/) {
1499 ((UKeywordsContext *)en->context)->current = ((UKeywordsContext *)en->context)->keywords;
1500 }
1501
1502 static const UEnumeration gKeywordsEnum = {
1503 NULL,
1504 NULL,
1505 uloc_kw_closeKeywords,
1506 uloc_kw_countKeywords,
1507 uenum_unextDefault,
1508 uloc_kw_nextKeyword,
1509 uloc_kw_resetKeywords
1510 };
1511
1512 U_CAPI UEnumeration* U_EXPORT2
uloc_openKeywordList(const char * keywordList,int32_t keywordListSize,UErrorCode * status)1513 uloc_openKeywordList(const char *keywordList, int32_t keywordListSize, UErrorCode* status)
1514 {
1515 UKeywordsContext *myContext = NULL;
1516 UEnumeration *result = NULL;
1517
1518 if(U_FAILURE(*status)) {
1519 return NULL;
1520 }
1521 result = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
1522 /* Null pointer test */
1523 if (result == NULL) {
1524 *status = U_MEMORY_ALLOCATION_ERROR;
1525 return NULL;
1526 }
1527 uprv_memcpy(result, &gKeywordsEnum, sizeof(UEnumeration));
1528 myContext = static_cast<UKeywordsContext *>(uprv_malloc(sizeof(UKeywordsContext)));
1529 if (myContext == NULL) {
1530 *status = U_MEMORY_ALLOCATION_ERROR;
1531 uprv_free(result);
1532 return NULL;
1533 }
1534 myContext->keywords = (char *)uprv_malloc(keywordListSize+1);
1535 uprv_memcpy(myContext->keywords, keywordList, keywordListSize);
1536 myContext->keywords[keywordListSize] = 0;
1537 myContext->current = myContext->keywords;
1538 result->context = myContext;
1539 return result;
1540 }
1541
1542 U_CAPI UEnumeration* U_EXPORT2
uloc_openKeywords(const char * localeID,UErrorCode * status)1543 uloc_openKeywords(const char* localeID,
1544 UErrorCode* status)
1545 {
1546 int32_t i=0;
1547 char keywords[256];
1548 int32_t keywordsCapacity = 256;
1549 char tempBuffer[ULOC_FULLNAME_CAPACITY];
1550 const char* tmpLocaleID;
1551
1552 if(status==NULL || U_FAILURE(*status)) {
1553 return 0;
1554 }
1555
1556 if (_hasBCP47Extension(localeID)) {
1557 _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), status);
1558 } else {
1559 if (localeID==NULL) {
1560 localeID=uloc_getDefault();
1561 }
1562 tmpLocaleID=localeID;
1563 }
1564
1565 /* Skip the language */
1566 ulocimp_getLanguage(tmpLocaleID, NULL, 0, &tmpLocaleID);
1567 if(_isIDSeparator(*tmpLocaleID)) {
1568 const char *scriptID;
1569 /* Skip the script if available */
1570 ulocimp_getScript(tmpLocaleID+1, NULL, 0, &scriptID);
1571 if(scriptID != tmpLocaleID+1) {
1572 /* Found optional script */
1573 tmpLocaleID = scriptID;
1574 }
1575 /* Skip the Country */
1576 if (_isIDSeparator(*tmpLocaleID)) {
1577 ulocimp_getCountry(tmpLocaleID+1, NULL, 0, &tmpLocaleID);
1578 if(_isIDSeparator(*tmpLocaleID)) {
1579 _getVariant(tmpLocaleID+1, *tmpLocaleID, NULL, 0);
1580 }
1581 }
1582 }
1583
1584 /* keywords are located after '@' */
1585 if((tmpLocaleID = locale_getKeywordsStart(tmpLocaleID)) != NULL) {
1586 i=locale_getKeywords(tmpLocaleID+1, '@', keywords, keywordsCapacity, NULL, 0, NULL, FALSE, status);
1587 }
1588
1589 if(i) {
1590 return uloc_openKeywordList(keywords, i, status);
1591 } else {
1592 return NULL;
1593 }
1594 }
1595
1596
1597 /* bit-flags for 'options' parameter of _canonicalize */
1598 #define _ULOC_STRIP_KEYWORDS 0x2
1599 #define _ULOC_CANONICALIZE 0x1
1600
1601 #define OPTION_SET(options, mask) ((options & mask) != 0)
1602
1603 static const char i_default[] = {'i', '-', 'd', 'e', 'f', 'a', 'u', 'l', 't'};
1604 #define I_DEFAULT_LENGTH (sizeof i_default / sizeof i_default[0])
1605
1606 /**
1607 * Canonicalize the given localeID, to level 1 or to level 2,
1608 * depending on the options. To specify level 1, pass in options=0.
1609 * To specify level 2, pass in options=_ULOC_CANONICALIZE.
1610 *
1611 * This is the code underlying uloc_getName and uloc_canonicalize.
1612 */
1613 static int32_t
_canonicalize(const char * localeID,char * result,int32_t resultCapacity,uint32_t options,UErrorCode * err)1614 _canonicalize(const char* localeID,
1615 char* result,
1616 int32_t resultCapacity,
1617 uint32_t options,
1618 UErrorCode* err) {
1619 int32_t j, len, fieldCount=0, scriptSize=0, variantSize=0, nameCapacity;
1620 char localeBuffer[ULOC_FULLNAME_CAPACITY];
1621 char tempBuffer[ULOC_FULLNAME_CAPACITY];
1622 const char* origLocaleID;
1623 const char* tmpLocaleID;
1624 const char* keywordAssign = NULL;
1625 const char* separatorIndicator = NULL;
1626 const char* addKeyword = NULL;
1627 const char* addValue = NULL;
1628 char* name;
1629 char* variant = NULL; /* pointer into name, or NULL */
1630
1631 if (U_FAILURE(*err)) {
1632 return 0;
1633 }
1634
1635 if (_hasBCP47Extension(localeID)) {
1636 _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), err);
1637 } else {
1638 if (localeID==NULL) {
1639 localeID=uloc_getDefault();
1640 }
1641 tmpLocaleID=localeID;
1642 }
1643
1644 origLocaleID=tmpLocaleID;
1645
1646 /* if we are doing a full canonicalization, then put results in
1647 localeBuffer, if necessary; otherwise send them to result. */
1648 if (/*OPTION_SET(options, _ULOC_CANONICALIZE) &&*/
1649 (result == NULL || resultCapacity < (int32_t)sizeof(localeBuffer))) {
1650 name = localeBuffer;
1651 nameCapacity = (int32_t)sizeof(localeBuffer);
1652 } else {
1653 name = result;
1654 nameCapacity = resultCapacity;
1655 }
1656
1657 /* get all pieces, one after another, and separate with '_' */
1658 len=ulocimp_getLanguage(tmpLocaleID, name, nameCapacity, &tmpLocaleID);
1659
1660 if(len == I_DEFAULT_LENGTH && uprv_strncmp(origLocaleID, i_default, len) == 0) {
1661 const char *d = uloc_getDefault();
1662
1663 len = (int32_t)uprv_strlen(d);
1664
1665 if (name != NULL) {
1666 uprv_strncpy(name, d, len);
1667 }
1668 } else if(_isIDSeparator(*tmpLocaleID)) {
1669 const char *scriptID;
1670
1671 ++fieldCount;
1672 if(len<nameCapacity) {
1673 name[len]='_';
1674 }
1675 ++len;
1676
1677 scriptSize=ulocimp_getScript(tmpLocaleID+1,
1678 (len<nameCapacity ? name+len : NULL), nameCapacity-len, &scriptID);
1679 if(scriptSize > 0) {
1680 /* Found optional script */
1681 tmpLocaleID = scriptID;
1682 ++fieldCount;
1683 len+=scriptSize;
1684 if (_isIDSeparator(*tmpLocaleID)) {
1685 /* If there is something else, then we add the _ */
1686 if(len<nameCapacity) {
1687 name[len]='_';
1688 }
1689 ++len;
1690 }
1691 }
1692
1693 if (_isIDSeparator(*tmpLocaleID)) {
1694 const char *cntryID;
1695 int32_t cntrySize = ulocimp_getCountry(tmpLocaleID+1,
1696 (len<nameCapacity ? name+len : NULL), nameCapacity-len, &cntryID);
1697 if (cntrySize > 0) {
1698 /* Found optional country */
1699 tmpLocaleID = cntryID;
1700 len+=cntrySize;
1701 }
1702 if(_isIDSeparator(*tmpLocaleID)) {
1703 /* If there is something else, then we add the _ if we found country before. */
1704 if (cntrySize >= 0 && ! _isIDSeparator(*(tmpLocaleID+1)) ) {
1705 ++fieldCount;
1706 if(len<nameCapacity) {
1707 name[len]='_';
1708 }
1709 ++len;
1710 }
1711
1712 variantSize = _getVariant(tmpLocaleID+1, *tmpLocaleID,
1713 (len<nameCapacity ? name+len : NULL), nameCapacity-len);
1714 if (variantSize > 0) {
1715 variant = len<nameCapacity ? name+len : NULL;
1716 len += variantSize;
1717 tmpLocaleID += variantSize + 1; /* skip '_' and variant */
1718 }
1719 }
1720 }
1721 }
1722
1723 /* Copy POSIX-style charset specifier, if any [mr.utf8] */
1724 if (!OPTION_SET(options, _ULOC_CANONICALIZE) && *tmpLocaleID == '.') {
1725 UBool done = FALSE;
1726 do {
1727 char c = *tmpLocaleID;
1728 switch (c) {
1729 case 0:
1730 case '@':
1731 done = TRUE;
1732 break;
1733 default:
1734 if (len<nameCapacity) {
1735 name[len] = c;
1736 }
1737 ++len;
1738 ++tmpLocaleID;
1739 break;
1740 }
1741 } while (!done);
1742 }
1743
1744 /* Scan ahead to next '@' and determine if it is followed by '=' and/or ';'
1745 After this, tmpLocaleID either points to '@' or is NULL */
1746 if ((tmpLocaleID=locale_getKeywordsStart(tmpLocaleID))!=NULL) {
1747 keywordAssign = uprv_strchr(tmpLocaleID, '=');
1748 separatorIndicator = uprv_strchr(tmpLocaleID, ';');
1749 }
1750
1751 /* Copy POSIX-style variant, if any [mr@FOO] */
1752 if (!OPTION_SET(options, _ULOC_CANONICALIZE) &&
1753 tmpLocaleID != NULL && keywordAssign == NULL) {
1754 for (;;) {
1755 char c = *tmpLocaleID;
1756 if (c == 0) {
1757 break;
1758 }
1759 if (len<nameCapacity) {
1760 name[len] = c;
1761 }
1762 ++len;
1763 ++tmpLocaleID;
1764 }
1765 }
1766
1767 if (OPTION_SET(options, _ULOC_CANONICALIZE)) {
1768 /* Handle @FOO variant if @ is present and not followed by = */
1769 if (tmpLocaleID!=NULL && keywordAssign==NULL) {
1770 int32_t posixVariantSize;
1771 /* Add missing '_' if needed */
1772 if (fieldCount < 2 || (fieldCount < 3 && scriptSize > 0)) {
1773 do {
1774 if(len<nameCapacity) {
1775 name[len]='_';
1776 }
1777 ++len;
1778 ++fieldCount;
1779 } while(fieldCount<2);
1780 }
1781 posixVariantSize = _getVariantEx(tmpLocaleID+1, '@', name+len, nameCapacity-len,
1782 (UBool)(variantSize > 0));
1783 if (posixVariantSize > 0) {
1784 if (variant == NULL) {
1785 variant = name+len;
1786 }
1787 len += posixVariantSize;
1788 variantSize += posixVariantSize;
1789 }
1790 }
1791
1792 /* Handle generic variants first */
1793 if (variant) {
1794 for (j=0; j<(int32_t)(sizeof(VARIANT_MAP)/sizeof(VARIANT_MAP[0])); j++) {
1795 const char* variantToCompare = VARIANT_MAP[j].variant;
1796 int32_t n = (int32_t)uprv_strlen(variantToCompare);
1797 int32_t variantLen = _deleteVariant(variant, uprv_min(variantSize, (nameCapacity-len)), variantToCompare, n);
1798 len -= variantLen;
1799 if (variantLen > 0) {
1800 if (len > 0 && name[len-1] == '_') { /* delete trailing '_' */
1801 --len;
1802 }
1803 addKeyword = VARIANT_MAP[j].keyword;
1804 addValue = VARIANT_MAP[j].value;
1805 break;
1806 }
1807 }
1808 if (len > 0 && len <= nameCapacity && name[len-1] == '_') { /* delete trailing '_' */
1809 --len;
1810 }
1811 }
1812
1813 /* Look up the ID in the canonicalization map */
1814 for (j=0; j<(int32_t)(sizeof(CANONICALIZE_MAP)/sizeof(CANONICALIZE_MAP[0])); j++) {
1815 const char* id = CANONICALIZE_MAP[j].id;
1816 int32_t n = (int32_t)uprv_strlen(id);
1817 if (len == n && uprv_strncmp(name, id, n) == 0) {
1818 if (n == 0 && tmpLocaleID != NULL) {
1819 break; /* Don't remap "" if keywords present */
1820 }
1821 len = _copyCount(name, nameCapacity, CANONICALIZE_MAP[j].canonicalID);
1822 if (CANONICALIZE_MAP[j].keyword) {
1823 addKeyword = CANONICALIZE_MAP[j].keyword;
1824 addValue = CANONICALIZE_MAP[j].value;
1825 }
1826 break;
1827 }
1828 }
1829 }
1830
1831 if (!OPTION_SET(options, _ULOC_STRIP_KEYWORDS)) {
1832 if (tmpLocaleID!=NULL && keywordAssign!=NULL &&
1833 (!separatorIndicator || separatorIndicator > keywordAssign)) {
1834 if(len<nameCapacity) {
1835 name[len]='@';
1836 }
1837 ++len;
1838 ++fieldCount;
1839 len += _getKeywords(tmpLocaleID+1, '@', (len<nameCapacity ? name+len : NULL), nameCapacity-len,
1840 NULL, 0, NULL, TRUE, addKeyword, addValue, err);
1841 } else if (addKeyword != NULL) {
1842 U_ASSERT(addValue != NULL && len < nameCapacity);
1843 /* inelegant but works -- later make _getKeywords do this? */
1844 len += _copyCount(name+len, nameCapacity-len, "@");
1845 len += _copyCount(name+len, nameCapacity-len, addKeyword);
1846 len += _copyCount(name+len, nameCapacity-len, "=");
1847 len += _copyCount(name+len, nameCapacity-len, addValue);
1848 }
1849 }
1850
1851 if (U_SUCCESS(*err) && result != NULL && name == localeBuffer) {
1852 uprv_strncpy(result, localeBuffer, (len > resultCapacity) ? resultCapacity : len);
1853 }
1854
1855 return u_terminateChars(result, resultCapacity, len, err);
1856 }
1857
1858 /* ### ID parsing API **************************************************/
1859
1860 U_CAPI int32_t U_EXPORT2
uloc_getParent(const char * localeID,char * parent,int32_t parentCapacity,UErrorCode * err)1861 uloc_getParent(const char* localeID,
1862 char* parent,
1863 int32_t parentCapacity,
1864 UErrorCode* err)
1865 {
1866 const char *lastUnderscore;
1867 int32_t i;
1868
1869 if (U_FAILURE(*err))
1870 return 0;
1871
1872 if (localeID == NULL)
1873 localeID = uloc_getDefault();
1874
1875 lastUnderscore=uprv_strrchr(localeID, '_');
1876 if(lastUnderscore!=NULL) {
1877 i=(int32_t)(lastUnderscore-localeID);
1878 } else {
1879 i=0;
1880 }
1881
1882 if(i>0 && parent != localeID) {
1883 uprv_memcpy(parent, localeID, uprv_min(i, parentCapacity));
1884 }
1885 return u_terminateChars(parent, parentCapacity, i, err);
1886 }
1887
1888 U_CAPI int32_t U_EXPORT2
uloc_getLanguage(const char * localeID,char * language,int32_t languageCapacity,UErrorCode * err)1889 uloc_getLanguage(const char* localeID,
1890 char* language,
1891 int32_t languageCapacity,
1892 UErrorCode* err)
1893 {
1894 /* uloc_getLanguage will return a 2 character iso-639 code if one exists. *CWB*/
1895 int32_t i=0;
1896
1897 if (err==NULL || U_FAILURE(*err)) {
1898 return 0;
1899 }
1900
1901 if(localeID==NULL) {
1902 localeID=uloc_getDefault();
1903 }
1904
1905 i=ulocimp_getLanguage(localeID, language, languageCapacity, NULL);
1906 return u_terminateChars(language, languageCapacity, i, err);
1907 }
1908
1909 U_CAPI int32_t U_EXPORT2
uloc_getScript(const char * localeID,char * script,int32_t scriptCapacity,UErrorCode * err)1910 uloc_getScript(const char* localeID,
1911 char* script,
1912 int32_t scriptCapacity,
1913 UErrorCode* err)
1914 {
1915 int32_t i=0;
1916
1917 if(err==NULL || U_FAILURE(*err)) {
1918 return 0;
1919 }
1920
1921 if(localeID==NULL) {
1922 localeID=uloc_getDefault();
1923 }
1924
1925 /* skip the language */
1926 ulocimp_getLanguage(localeID, NULL, 0, &localeID);
1927 if(_isIDSeparator(*localeID)) {
1928 i=ulocimp_getScript(localeID+1, script, scriptCapacity, NULL);
1929 }
1930 return u_terminateChars(script, scriptCapacity, i, err);
1931 }
1932
1933 U_CAPI int32_t U_EXPORT2
uloc_getCountry(const char * localeID,char * country,int32_t countryCapacity,UErrorCode * err)1934 uloc_getCountry(const char* localeID,
1935 char* country,
1936 int32_t countryCapacity,
1937 UErrorCode* err)
1938 {
1939 int32_t i=0;
1940
1941 if(err==NULL || U_FAILURE(*err)) {
1942 return 0;
1943 }
1944
1945 if(localeID==NULL) {
1946 localeID=uloc_getDefault();
1947 }
1948
1949 /* Skip the language */
1950 ulocimp_getLanguage(localeID, NULL, 0, &localeID);
1951 if(_isIDSeparator(*localeID)) {
1952 const char *scriptID;
1953 /* Skip the script if available */
1954 ulocimp_getScript(localeID+1, NULL, 0, &scriptID);
1955 if(scriptID != localeID+1) {
1956 /* Found optional script */
1957 localeID = scriptID;
1958 }
1959 if(_isIDSeparator(*localeID)) {
1960 i=ulocimp_getCountry(localeID+1, country, countryCapacity, NULL);
1961 }
1962 }
1963 return u_terminateChars(country, countryCapacity, i, err);
1964 }
1965
1966 U_CAPI int32_t U_EXPORT2
uloc_getVariant(const char * localeID,char * variant,int32_t variantCapacity,UErrorCode * err)1967 uloc_getVariant(const char* localeID,
1968 char* variant,
1969 int32_t variantCapacity,
1970 UErrorCode* err)
1971 {
1972 char tempBuffer[ULOC_FULLNAME_CAPACITY];
1973 const char* tmpLocaleID;
1974 int32_t i=0;
1975
1976 if(err==NULL || U_FAILURE(*err)) {
1977 return 0;
1978 }
1979
1980 if (_hasBCP47Extension(localeID)) {
1981 _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), err);
1982 } else {
1983 if (localeID==NULL) {
1984 localeID=uloc_getDefault();
1985 }
1986 tmpLocaleID=localeID;
1987 }
1988
1989 /* Skip the language */
1990 ulocimp_getLanguage(tmpLocaleID, NULL, 0, &tmpLocaleID);
1991 if(_isIDSeparator(*tmpLocaleID)) {
1992 const char *scriptID;
1993 /* Skip the script if available */
1994 ulocimp_getScript(tmpLocaleID+1, NULL, 0, &scriptID);
1995 if(scriptID != tmpLocaleID+1) {
1996 /* Found optional script */
1997 tmpLocaleID = scriptID;
1998 }
1999 /* Skip the Country */
2000 if (_isIDSeparator(*tmpLocaleID)) {
2001 const char *cntryID;
2002 ulocimp_getCountry(tmpLocaleID+1, NULL, 0, &cntryID);
2003 if (cntryID != tmpLocaleID+1) {
2004 /* Found optional country */
2005 tmpLocaleID = cntryID;
2006 }
2007 if(_isIDSeparator(*tmpLocaleID)) {
2008 /* If there was no country ID, skip a possible extra IDSeparator */
2009 if (tmpLocaleID != cntryID && _isIDSeparator(tmpLocaleID[1])) {
2010 tmpLocaleID++;
2011 }
2012 i=_getVariant(tmpLocaleID+1, *tmpLocaleID, variant, variantCapacity);
2013 }
2014 }
2015 }
2016
2017 /* removed by weiv. We don't want to handle POSIX variants anymore. Use canonicalization function */
2018 /* if we do not have a variant tag yet then try a POSIX variant after '@' */
2019 /*
2020 if(!haveVariant && (localeID=uprv_strrchr(localeID, '@'))!=NULL) {
2021 i=_getVariant(localeID+1, '@', variant, variantCapacity);
2022 }
2023 */
2024 return u_terminateChars(variant, variantCapacity, i, err);
2025 }
2026
2027 U_CAPI int32_t U_EXPORT2
uloc_getName(const char * localeID,char * name,int32_t nameCapacity,UErrorCode * err)2028 uloc_getName(const char* localeID,
2029 char* name,
2030 int32_t nameCapacity,
2031 UErrorCode* err)
2032 {
2033 return _canonicalize(localeID, name, nameCapacity, 0, err);
2034 }
2035
2036 U_CAPI int32_t U_EXPORT2
uloc_getBaseName(const char * localeID,char * name,int32_t nameCapacity,UErrorCode * err)2037 uloc_getBaseName(const char* localeID,
2038 char* name,
2039 int32_t nameCapacity,
2040 UErrorCode* err)
2041 {
2042 return _canonicalize(localeID, name, nameCapacity, _ULOC_STRIP_KEYWORDS, err);
2043 }
2044
2045 U_CAPI int32_t U_EXPORT2
uloc_canonicalize(const char * localeID,char * name,int32_t nameCapacity,UErrorCode * err)2046 uloc_canonicalize(const char* localeID,
2047 char* name,
2048 int32_t nameCapacity,
2049 UErrorCode* err)
2050 {
2051 return _canonicalize(localeID, name, nameCapacity, _ULOC_CANONICALIZE, err);
2052 }
2053
2054 U_CAPI const char* U_EXPORT2
uloc_getISO3Language(const char * localeID)2055 uloc_getISO3Language(const char* localeID)
2056 {
2057 int16_t offset;
2058 char lang[ULOC_LANG_CAPACITY];
2059 UErrorCode err = U_ZERO_ERROR;
2060
2061 if (localeID == NULL)
2062 {
2063 localeID = uloc_getDefault();
2064 }
2065 uloc_getLanguage(localeID, lang, ULOC_LANG_CAPACITY, &err);
2066 if (U_FAILURE(err))
2067 return "";
2068 offset = _findIndex(LANGUAGES, lang);
2069 if (offset < 0)
2070 return "";
2071 return LANGUAGES_3[offset];
2072 }
2073
2074 U_CAPI const char* U_EXPORT2
uloc_getISO3Country(const char * localeID)2075 uloc_getISO3Country(const char* localeID)
2076 {
2077 int16_t offset;
2078 char cntry[ULOC_LANG_CAPACITY];
2079 UErrorCode err = U_ZERO_ERROR;
2080
2081 if (localeID == NULL)
2082 {
2083 localeID = uloc_getDefault();
2084 }
2085 uloc_getCountry(localeID, cntry, ULOC_LANG_CAPACITY, &err);
2086 if (U_FAILURE(err))
2087 return "";
2088 offset = _findIndex(COUNTRIES, cntry);
2089 if (offset < 0)
2090 return "";
2091
2092 return COUNTRIES_3[offset];
2093 }
2094
2095 U_CAPI uint32_t U_EXPORT2
uloc_getLCID(const char * localeID)2096 uloc_getLCID(const char* localeID)
2097 {
2098 UErrorCode status = U_ZERO_ERROR;
2099 char langID[ULOC_FULLNAME_CAPACITY];
2100
2101 uloc_getLanguage(localeID, langID, sizeof(langID), &status);
2102 if (U_FAILURE(status)) {
2103 return 0;
2104 }
2105
2106 return uprv_convertToLCID(langID, localeID, &status);
2107 }
2108
2109 U_CAPI int32_t U_EXPORT2
uloc_getLocaleForLCID(uint32_t hostid,char * locale,int32_t localeCapacity,UErrorCode * status)2110 uloc_getLocaleForLCID(uint32_t hostid, char *locale, int32_t localeCapacity,
2111 UErrorCode *status)
2112 {
2113 int32_t length;
2114 const char *posix = uprv_convertToPosix(hostid, status);
2115 if (U_FAILURE(*status) || posix == NULL) {
2116 return 0;
2117 }
2118 length = (int32_t)uprv_strlen(posix);
2119 if (length+1 > localeCapacity) {
2120 *status = U_BUFFER_OVERFLOW_ERROR;
2121 }
2122 else {
2123 uprv_strcpy(locale, posix);
2124 }
2125 return length;
2126 }
2127
2128 /* ### Default locale **************************************************/
2129
2130 U_CAPI const char* U_EXPORT2
uloc_getDefault()2131 uloc_getDefault()
2132 {
2133 return locale_get_default();
2134 }
2135
2136 U_CAPI void U_EXPORT2
uloc_setDefault(const char * newDefaultLocale,UErrorCode * err)2137 uloc_setDefault(const char* newDefaultLocale,
2138 UErrorCode* err)
2139 {
2140 if (U_FAILURE(*err))
2141 return;
2142 /* the error code isn't currently used for anything by this function*/
2143
2144 /* propagate change to C++ */
2145 locale_set_default(newDefaultLocale);
2146 }
2147
2148 /**
2149 * Returns a list of all language codes defined in ISO 639. This is a pointer
2150 * to an array of pointers to arrays of char. All of these pointers are owned
2151 * by ICU-- do not delete them, and do not write through them. The array is
2152 * terminated with a null pointer.
2153 */
2154 U_CAPI const char* const* U_EXPORT2
uloc_getISOLanguages()2155 uloc_getISOLanguages()
2156 {
2157 return LANGUAGES;
2158 }
2159
2160 /**
2161 * Returns a list of all 2-letter country codes defined in ISO 639. This is a
2162 * pointer to an array of pointers to arrays of char. All of these pointers are
2163 * owned by ICU-- do not delete them, and do not write through them. The array is
2164 * terminated with a null pointer.
2165 */
2166 U_CAPI const char* const* U_EXPORT2
uloc_getISOCountries()2167 uloc_getISOCountries()
2168 {
2169 return COUNTRIES;
2170 }
2171
2172
2173 /* this function to be moved into cstring.c later */
2174 static char gDecimal = 0;
2175
2176 static /* U_CAPI */
2177 double
2178 /* U_EXPORT2 */
_uloc_strtod(const char * start,char ** end)2179 _uloc_strtod(const char *start, char **end) {
2180 char *decimal;
2181 char *myEnd;
2182 char buf[30];
2183 double rv;
2184 if (!gDecimal) {
2185 char rep[5];
2186 /* For machines that decide to change the decimal on you,
2187 and try to be too smart with localization.
2188 This normally should be just a '.'. */
2189 sprintf(rep, "%+1.1f", 1.0);
2190 gDecimal = rep[2];
2191 }
2192
2193 if(gDecimal == '.') {
2194 return uprv_strtod(start, end); /* fall through to OS */
2195 } else {
2196 uprv_strncpy(buf, start, 29);
2197 buf[29]=0;
2198 decimal = uprv_strchr(buf, '.');
2199 if(decimal) {
2200 *decimal = gDecimal;
2201 } else {
2202 return uprv_strtod(start, end); /* no decimal point */
2203 }
2204 rv = uprv_strtod(buf, &myEnd);
2205 if(end) {
2206 *end = (char*)(start+(myEnd-buf)); /* cast away const (to follow uprv_strtod API.) */
2207 }
2208 return rv;
2209 }
2210 }
2211
2212 typedef struct {
2213 float q;
2214 int32_t dummy; /* to avoid uninitialized memory copy from qsort */
2215 char *locale;
2216 } _acceptLangItem;
2217
2218 static int32_t U_CALLCONV
uloc_acceptLanguageCompare(const void *,const void * a,const void * b)2219 uloc_acceptLanguageCompare(const void * /*context*/, const void *a, const void *b)
2220 {
2221 const _acceptLangItem *aa = (const _acceptLangItem*)a;
2222 const _acceptLangItem *bb = (const _acceptLangItem*)b;
2223
2224 int32_t rc = 0;
2225 if(bb->q < aa->q) {
2226 rc = -1; /* A > B */
2227 } else if(bb->q > aa->q) {
2228 rc = 1; /* A < B */
2229 } else {
2230 rc = 0; /* A = B */
2231 }
2232
2233 if(rc==0) {
2234 rc = uprv_stricmp(aa->locale, bb->locale);
2235 }
2236
2237 #if defined(ULOC_DEBUG)
2238 /* fprintf(stderr, "a:[%s:%g], b:[%s:%g] -> %d\n",
2239 aa->locale, aa->q,
2240 bb->locale, bb->q,
2241 rc);*/
2242 #endif
2243
2244 return rc;
2245 }
2246
2247 /*
2248 mt-mt, ja;q=0.76, en-us;q=0.95, en;q=0.92, en-gb;q=0.89, fr;q=0.87, iu-ca;q=0.84, iu;q=0.82, ja-jp;q=0.79, mt;q=0.97, de-de;q=0.74, de;q=0.71, es;q=0.68, it-it;q=0.66, it;q=0.63, vi-vn;q=0.61, vi;q=0.58, nl-nl;q=0.55, nl;q=0.53
2249 */
2250
2251 U_CAPI int32_t U_EXPORT2
uloc_acceptLanguageFromHTTP(char * result,int32_t resultAvailable,UAcceptResult * outResult,const char * httpAcceptLanguage,UEnumeration * availableLocales,UErrorCode * status)2252 uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable, UAcceptResult *outResult,
2253 const char *httpAcceptLanguage,
2254 UEnumeration* availableLocales,
2255 UErrorCode *status)
2256 {
2257 _acceptLangItem *j;
2258 _acceptLangItem smallBuffer[30];
2259 char **strs;
2260 char tmp[ULOC_FULLNAME_CAPACITY +1];
2261 int32_t n = 0;
2262 const char *itemEnd;
2263 const char *paramEnd;
2264 const char *s;
2265 const char *t;
2266 int32_t res;
2267 int32_t i;
2268 int32_t l = (int32_t)uprv_strlen(httpAcceptLanguage);
2269 int32_t jSize;
2270 char *tempstr; /* Use for null pointer check */
2271
2272 j = smallBuffer;
2273 jSize = sizeof(smallBuffer)/sizeof(smallBuffer[0]);
2274 if(U_FAILURE(*status)) {
2275 return -1;
2276 }
2277
2278 for(s=httpAcceptLanguage;s&&*s;) {
2279 while(isspace(*s)) /* eat space at the beginning */
2280 s++;
2281 itemEnd=uprv_strchr(s,',');
2282 paramEnd=uprv_strchr(s,';');
2283 if(!itemEnd) {
2284 itemEnd = httpAcceptLanguage+l; /* end of string */
2285 }
2286 if(paramEnd && paramEnd<itemEnd) {
2287 /* semicolon (;) is closer than end (,) */
2288 t = paramEnd+1;
2289 if(*t=='q') {
2290 t++;
2291 }
2292 while(isspace(*t)) {
2293 t++;
2294 }
2295 if(*t=='=') {
2296 t++;
2297 }
2298 while(isspace(*t)) {
2299 t++;
2300 }
2301 j[n].q = (float)_uloc_strtod(t,NULL);
2302 } else {
2303 /* no semicolon - it's 1.0 */
2304 j[n].q = 1.0f;
2305 paramEnd = itemEnd;
2306 }
2307 j[n].dummy=0;
2308 /* eat spaces prior to semi */
2309 for(t=(paramEnd-1);(paramEnd>s)&&isspace(*t);t--)
2310 ;
2311 /* Check for null pointer from uprv_strndup */
2312 tempstr = uprv_strndup(s,(int32_t)((t+1)-s));
2313 if (tempstr == NULL) {
2314 *status = U_MEMORY_ALLOCATION_ERROR;
2315 return -1;
2316 }
2317 j[n].locale = tempstr;
2318 uloc_canonicalize(j[n].locale,tmp,sizeof(tmp)/sizeof(tmp[0]),status);
2319 if(strcmp(j[n].locale,tmp)) {
2320 uprv_free(j[n].locale);
2321 j[n].locale=uprv_strdup(tmp);
2322 }
2323 #if defined(ULOC_DEBUG)
2324 /*fprintf(stderr,"%d: s <%s> q <%g>\n", n, j[n].locale, j[n].q);*/
2325 #endif
2326 n++;
2327 s = itemEnd;
2328 while(*s==',') { /* eat duplicate commas */
2329 s++;
2330 }
2331 if(n>=jSize) {
2332 if(j==smallBuffer) { /* overflowed the small buffer. */
2333 j = static_cast<_acceptLangItem *>(uprv_malloc(sizeof(j[0])*(jSize*2)));
2334 if(j!=NULL) {
2335 uprv_memcpy(j,smallBuffer,sizeof(j[0])*jSize);
2336 }
2337 #if defined(ULOC_DEBUG)
2338 fprintf(stderr,"malloced at size %d\n", jSize);
2339 #endif
2340 } else {
2341 j = static_cast<_acceptLangItem *>(uprv_realloc(j, sizeof(j[0])*jSize*2));
2342 #if defined(ULOC_DEBUG)
2343 fprintf(stderr,"re-alloced at size %d\n", jSize);
2344 #endif
2345 }
2346 jSize *= 2;
2347 if(j==NULL) {
2348 *status = U_MEMORY_ALLOCATION_ERROR;
2349 return -1;
2350 }
2351 }
2352 }
2353 uprv_sortArray(j, n, sizeof(j[0]), uloc_acceptLanguageCompare, NULL, TRUE, status);
2354 if(U_FAILURE(*status)) {
2355 if(j != smallBuffer) {
2356 #if defined(ULOC_DEBUG)
2357 fprintf(stderr,"freeing j %p\n", j);
2358 #endif
2359 uprv_free(j);
2360 }
2361 return -1;
2362 }
2363 strs = static_cast<char **>(uprv_malloc((size_t)(sizeof(strs[0])*n)));
2364 /* Check for null pointer */
2365 if (strs == NULL) {
2366 uprv_free(j); /* Free to avoid memory leak */
2367 *status = U_MEMORY_ALLOCATION_ERROR;
2368 return -1;
2369 }
2370 for(i=0;i<n;i++) {
2371 #if defined(ULOC_DEBUG)
2372 /*fprintf(stderr,"%d: s <%s> q <%g>\n", i, j[i].locale, j[i].q);*/
2373 #endif
2374 strs[i]=j[i].locale;
2375 }
2376 res = uloc_acceptLanguage(result, resultAvailable, outResult,
2377 (const char**)strs, n, availableLocales, status);
2378 for(i=0;i<n;i++) {
2379 uprv_free(strs[i]);
2380 }
2381 uprv_free(strs);
2382 if(j != smallBuffer) {
2383 #if defined(ULOC_DEBUG)
2384 fprintf(stderr,"freeing j %p\n", j);
2385 #endif
2386 uprv_free(j);
2387 }
2388 return res;
2389 }
2390
2391
2392 U_CAPI int32_t U_EXPORT2
uloc_acceptLanguage(char * result,int32_t resultAvailable,UAcceptResult * outResult,const char ** acceptList,int32_t acceptListCount,UEnumeration * availableLocales,UErrorCode * status)2393 uloc_acceptLanguage(char *result, int32_t resultAvailable,
2394 UAcceptResult *outResult, const char **acceptList,
2395 int32_t acceptListCount,
2396 UEnumeration* availableLocales,
2397 UErrorCode *status)
2398 {
2399 int32_t i,j;
2400 int32_t len;
2401 int32_t maxLen=0;
2402 char tmp[ULOC_FULLNAME_CAPACITY+1];
2403 const char *l;
2404 char **fallbackList;
2405 if(U_FAILURE(*status)) {
2406 return -1;
2407 }
2408 fallbackList = static_cast<char **>(uprv_malloc((size_t)(sizeof(fallbackList[0])*acceptListCount)));
2409 if(fallbackList==NULL) {
2410 *status = U_MEMORY_ALLOCATION_ERROR;
2411 return -1;
2412 }
2413 for(i=0;i<acceptListCount;i++) {
2414 #if defined(ULOC_DEBUG)
2415 fprintf(stderr,"%02d: %s\n", i, acceptList[i]);
2416 #endif
2417 while((l=uenum_next(availableLocales, NULL, status))) {
2418 #if defined(ULOC_DEBUG)
2419 fprintf(stderr," %s\n", l);
2420 #endif
2421 len = (int32_t)uprv_strlen(l);
2422 if(!uprv_strcmp(acceptList[i], l)) {
2423 if(outResult) {
2424 *outResult = ULOC_ACCEPT_VALID;
2425 }
2426 #if defined(ULOC_DEBUG)
2427 fprintf(stderr, "MATCH! %s\n", l);
2428 #endif
2429 if(len>0) {
2430 uprv_strncpy(result, l, uprv_min(len, resultAvailable));
2431 }
2432 for(j=0;j<i;j++) {
2433 uprv_free(fallbackList[j]);
2434 }
2435 uprv_free(fallbackList);
2436 return u_terminateChars(result, resultAvailable, len, status);
2437 }
2438 if(len>maxLen) {
2439 maxLen = len;
2440 }
2441 }
2442 uenum_reset(availableLocales, status);
2443 /* save off parent info */
2444 if(uloc_getParent(acceptList[i], tmp, sizeof(tmp)/sizeof(tmp[0]), status)!=0) {
2445 fallbackList[i] = uprv_strdup(tmp);
2446 } else {
2447 fallbackList[i]=0;
2448 }
2449 }
2450
2451 for(maxLen--;maxLen>0;maxLen--) {
2452 for(i=0;i<acceptListCount;i++) {
2453 if(fallbackList[i] && ((int32_t)uprv_strlen(fallbackList[i])==maxLen)) {
2454 #if defined(ULOC_DEBUG)
2455 fprintf(stderr,"Try: [%s]", fallbackList[i]);
2456 #endif
2457 while((l=uenum_next(availableLocales, NULL, status))) {
2458 #if defined(ULOC_DEBUG)
2459 fprintf(stderr," %s\n", l);
2460 #endif
2461 len = (int32_t)uprv_strlen(l);
2462 if(!uprv_strcmp(fallbackList[i], l)) {
2463 if(outResult) {
2464 *outResult = ULOC_ACCEPT_FALLBACK;
2465 }
2466 #if defined(ULOC_DEBUG)
2467 fprintf(stderr, "fallback MATCH! %s\n", l);
2468 #endif
2469 if(len>0) {
2470 uprv_strncpy(result, l, uprv_min(len, resultAvailable));
2471 }
2472 for(j=0;j<acceptListCount;j++) {
2473 uprv_free(fallbackList[j]);
2474 }
2475 uprv_free(fallbackList);
2476 return u_terminateChars(result, resultAvailable, len, status);
2477 }
2478 }
2479 uenum_reset(availableLocales, status);
2480
2481 if(uloc_getParent(fallbackList[i], tmp, sizeof(tmp)/sizeof(tmp[0]), status)!=0) {
2482 uprv_free(fallbackList[i]);
2483 fallbackList[i] = uprv_strdup(tmp);
2484 } else {
2485 uprv_free(fallbackList[i]);
2486 fallbackList[i]=0;
2487 }
2488 }
2489 }
2490 if(outResult) {
2491 *outResult = ULOC_ACCEPT_FAILED;
2492 }
2493 }
2494 for(i=0;i<acceptListCount;i++) {
2495 uprv_free(fallbackList[i]);
2496 }
2497 uprv_free(fallbackList);
2498 return -1;
2499 }
2500
2501 /*eof*/
2502