1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/common/uloc.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2528 @@ 1.4 +/* 1.5 +********************************************************************** 1.6 +* Copyright (C) 1997-2013, International Business Machines 1.7 +* Corporation and others. All Rights Reserved. 1.8 +********************************************************************** 1.9 +* 1.10 +* File ULOC.CPP 1.11 +* 1.12 +* Modification History: 1.13 +* 1.14 +* Date Name Description 1.15 +* 04/01/97 aliu Creation. 1.16 +* 08/21/98 stephen JDK 1.2 sync 1.17 +* 12/08/98 rtg New Locale implementation and C API 1.18 +* 03/15/99 damiba overhaul. 1.19 +* 04/06/99 stephen changed setDefault() to realloc and copy 1.20 +* 06/14/99 stephen Changed calls to ures_open for new params 1.21 +* 07/21/99 stephen Modified setDefault() to propagate to C++ 1.22 +* 05/14/04 alan 7 years later: refactored, cleaned up, fixed bugs, 1.23 +* brought canonicalization code into line with spec 1.24 +*****************************************************************************/ 1.25 + 1.26 +/* 1.27 + POSIX's locale format, from putil.c: [no spaces] 1.28 + 1.29 + ll [ _CC ] [ . MM ] [ @ VV] 1.30 + 1.31 + l = lang, C = ctry, M = charmap, V = variant 1.32 +*/ 1.33 + 1.34 +#include "unicode/utypes.h" 1.35 +#include "unicode/ustring.h" 1.36 +#include "unicode/uloc.h" 1.37 + 1.38 +#include "putilimp.h" 1.39 +#include "ustr_imp.h" 1.40 +#include "ulocimp.h" 1.41 +#include "umutex.h" 1.42 +#include "cstring.h" 1.43 +#include "cmemory.h" 1.44 +#include "ucln_cmn.h" 1.45 +#include "locmap.h" 1.46 +#include "uarrsort.h" 1.47 +#include "uenumimp.h" 1.48 +#include "uassert.h" 1.49 + 1.50 +#include <stdio.h> /* for sprintf */ 1.51 + 1.52 +/* ### Declarations **************************************************/ 1.53 + 1.54 +/* Locale stuff from locid.cpp */ 1.55 +U_CFUNC void locale_set_default(const char *id); 1.56 +U_CFUNC const char *locale_get_default(void); 1.57 +U_CFUNC int32_t 1.58 +locale_getKeywords(const char *localeID, 1.59 + char prev, 1.60 + char *keywords, int32_t keywordCapacity, 1.61 + char *values, int32_t valuesCapacity, int32_t *valLen, 1.62 + UBool valuesToo, 1.63 + UErrorCode *status); 1.64 + 1.65 +/* ### Data tables **************************************************/ 1.66 + 1.67 +/** 1.68 + * Table of language codes, both 2- and 3-letter, with preference 1.69 + * given to 2-letter codes where possible. Includes 3-letter codes 1.70 + * that lack a 2-letter equivalent. 1.71 + * 1.72 + * This list must be in sorted order. This list is returned directly 1.73 + * to the user by some API. 1.74 + * 1.75 + * This list must be kept in sync with LANGUAGES_3, with corresponding 1.76 + * entries matched. 1.77 + * 1.78 + * This table should be terminated with a NULL entry, followed by a 1.79 + * second list, and another NULL entry. The first list is visible to 1.80 + * user code when this array is returned by API. The second list 1.81 + * contains codes we support, but do not expose through user API. 1.82 + * 1.83 + * Notes 1.84 + * 1.85 + * Tables updated per http://lcweb.loc.gov/standards/iso639-2/ to 1.86 + * include the revisions up to 2001/7/27 *CWB* 1.87 + * 1.88 + * The 3 character codes are the terminology codes like RFC 3066. This 1.89 + * is compatible with prior ICU codes 1.90 + * 1.91 + * "in" "iw" "ji" "jw" & "sh" have been withdrawn but are still in the 1.92 + * table but now at the end of the table because 3 character codes are 1.93 + * duplicates. This avoids bad searches going from 3 to 2 character 1.94 + * codes. 1.95 + * 1.96 + * The range qaa-qtz is reserved for local use 1.97 + */ 1.98 +/* Generated using org.unicode.cldr.icu.GenerateISO639LanguageTables */ 1.99 +/* ISO639 table version is 20130531 */ 1.100 +static const char * const LANGUAGES[] = { 1.101 + "aa", "ab", "ace", "ach", "ada", "ady", "ae", "af", 1.102 + "afa", "afh", "agq", "ain", "ak", "akk", "ale", "alg", 1.103 + "alt", "am", "an", "ang", "anp", "apa", "ar", "arc", 1.104 + "arn", "arp", "art", "arw", "as", "asa", "ast", "ath", 1.105 + "aus", "av", "awa", "ay", "az", 1.106 + "ba", "bad", "bai", "bal", "ban", "bas", "bat", "bax", 1.107 + "bbj", "be", "bej", "bem", "ber", "bez", "bfd", "bg", 1.108 + "bh", "bho", "bi", "bik", "bin", "bkm", "bla", "bm", 1.109 + "bn", "bnt", "bo", "br", "bra", "brx", "bs", "bss", 1.110 + "btk", "bua", "bug", "bum", "byn", "byv", 1.111 + "ca", "cad", "cai", "car", "cau", "cay", "cch", "ce", 1.112 + "ceb", "cel", "cgg", "ch", "chb", "chg", "chk", "chm", 1.113 + "chn", "cho", "chp", "chr", "chy", "ckb", "cmc", "co", 1.114 + "cop", "cpe", "cpf", "cpp", "cr", "crh", "crp", "cs", 1.115 + "csb", "cu", "cus", "cv", "cy", 1.116 + "da", "dak", "dar", "dav", "day", "de", "del", "den", 1.117 + "dgr", "din", "dje", "doi", "dra", "dsb", "dua", "dum", 1.118 + "dv", "dyo", "dyu", "dz", "dzg", 1.119 + "ebu", "ee", "efi", "egy", "eka", "el", "elx", "en", 1.120 + "enm", "eo", "es", "et", "eu", "ewo", 1.121 + "fa", "fan", "fat", "ff", "fi", "fil", "fiu", "fj", 1.122 + "fo", "fon", "fr", "frm", "fro", "frr", "frs", "fur", 1.123 + "fy", 1.124 + "ga", "gaa", "gay", "gba", "gd", "gem", "gez", "gil", 1.125 + "gl", "gmh", "gn", "goh", "gon", "gor", "got", "grb", 1.126 + "grc", "gsw", "gu", "guz", "gv", "gwi", 1.127 + "ha", "hai", "haw", "he", "hi", "hil", "him", "hit", 1.128 + "hmn", "ho", "hr", "hsb", "ht", "hu", "hup", "hy", 1.129 + "hz", 1.130 + "ia", "iba", "ibb", "id", "ie", "ig", "ii", "ijo", 1.131 + "ik", "ilo", "inc", "ine", "inh", "io", "ira", "iro", 1.132 + "is", "it", "iu", 1.133 + "ja", "jbo", "jgo", "jmc", "jpr", "jrb", "jv", 1.134 + "ka", "kaa", "kab", "kac", "kaj", "kam", "kar", "kaw", 1.135 + "kbd", "kbl", "kcg", "kde", "kea", "kfo", "kg", "kha", 1.136 + "khi", "kho", "khq", "ki", "kj", "kk", "kkj", "kl", 1.137 + "kln", "km", "kmb", "kn", "ko", "kok", "kos", "kpe", 1.138 + "kr", "krc", "krl", "kro", "kru", "ks", "ksb", "ksf", 1.139 + "ksh", "ku", "kum", "kut", "kv", "kw", "ky", 1.140 + "la", "lad", "lag", "lah", "lam", "lb", "lez", "lg", 1.141 + "li", "lkt", "ln", "lo", "lol", "loz", "lt", "lu", 1.142 + "lua", "lui", "lun", "luo", "lus", "luy", "lv", 1.143 + "mad", "maf", "mag", "mai", "mak", "man", "map", "mas", 1.144 + "mde", "mdf", "mdr", "men", "mer", "mfe", "mg", "mga", 1.145 + "mgh", "mgo", "mh", "mi", "mic", "min", "mis", "mk", 1.146 + "mkh", "ml", "mn", "mnc", "mni", "mno", "mo", "moh", 1.147 + "mos", "mr", "ms", "mt", "mua", "mul", "mun", "mus", 1.148 + "mwl", "mwr", "my", "mye", "myn", "myv", 1.149 + "na", "nah", "nai", "nap", "naq", "nb", "nd", "nds", 1.150 + "ne", "new", "ng", "nia", "nic", "niu", "nl", "nmg", 1.151 + "nn", "nnh", "no", "nog", "non", "nqo", "nr", "nso", 1.152 + "nub", "nus", "nv", "nwc", "ny", "nym", "nyn", "nyo", 1.153 + "nzi", 1.154 + "oc", "oj", "om", "or", "os", "osa", "ota", "oto", 1.155 + "pa", "paa", "pag", "pal", "pam", "pap", "pau", "peo", 1.156 + "phi", "phn", "pi", "pl", "pon", "pra", "pro", "ps", 1.157 + "pt", 1.158 + "qu", 1.159 + "raj", "rap", "rar", "rm", "rn", "ro", "roa", "rof", 1.160 + "rom", "ru", "rup", "rw", "rwk", 1.161 + "sa", "sad", "sah", "sai", "sal", "sam", "saq", "sas", 1.162 + "sat", "sba", "sbp", "sc", "scn", "sco", "sd", "se", 1.163 + "see", "seh", "sel", "sem", "ses", "sg", "sga", "sgn", 1.164 + "shi", "shn", "shu", "si", "sid", "sio", "sit", 1.165 + "sk", "sl", "sla", "sm", "sma", "smi", "smj", "smn", 1.166 + "sms", "sn", "snk", "so", "sog", "son", "sq", "sr", 1.167 + "srn", "srr", "ss", "ssa", "ssy", "st", "su", "suk", 1.168 + "sus", "sux", "sv", "sw", "swb", "swc", "syc", "syr", 1.169 + "ta", "tai", "te", "tem", "teo", "ter", "tet", "tg", 1.170 + "th", "ti", "tig", "tiv", "tk", "tkl", "tl", "tlh", 1.171 + "tli", "tmh", "tn", "to", "tog", "tpi", "tr", "trv", 1.172 + "ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw", 1.173 + "twq", "ty", "tyv", "tzm", 1.174 + "udm", "ug", "uga", "uk", "umb", "und", "ur", "uz", 1.175 + "vai", "ve", "vi", "vo", "vot", "vun", 1.176 + "wa", "wae", "wak", "wal", "war", "was", "wen", "wo", 1.177 + "xal", "xh", "xog", 1.178 + "yao", "yap", "yav", "ybb", "yi", "yo", "ypk", "yue", 1.179 + "za", "zap", "zbl", "zen", "zgh", "zh", "znd", "zu", 1.180 + "zun", "zxx", "zza", 1.181 +NULL, 1.182 + "in", "iw", "ji", "jw", "sh", /* obsolete language codes */ 1.183 +NULL 1.184 +}; 1.185 + 1.186 +static const char* const DEPRECATED_LANGUAGES[]={ 1.187 + "in", "iw", "ji", "jw", NULL, NULL 1.188 +}; 1.189 +static const char* const REPLACEMENT_LANGUAGES[]={ 1.190 + "id", "he", "yi", "jv", NULL, NULL 1.191 +}; 1.192 + 1.193 +/** 1.194 + * Table of 3-letter language codes. 1.195 + * 1.196 + * This is a lookup table used to convert 3-letter language codes to 1.197 + * their 2-letter equivalent, where possible. It must be kept in sync 1.198 + * with LANGUAGES. For all valid i, LANGUAGES[i] must refer to the 1.199 + * same language as LANGUAGES_3[i]. The commented-out lines are 1.200 + * copied from LANGUAGES to make eyeballing this baby easier. 1.201 + * 1.202 + * Where a 3-letter language code has no 2-letter equivalent, the 1.203 + * 3-letter code occupies both LANGUAGES[i] and LANGUAGES_3[i]. 1.204 + * 1.205 + * This table should be terminated with a NULL entry, followed by a 1.206 + * second list, and another NULL entry. The two lists correspond to 1.207 + * the two lists in LANGUAGES. 1.208 + */ 1.209 +/* Generated using org.unicode.cldr.icu.GenerateISO639LanguageTables */ 1.210 +/* ISO639 table version is 20130531 */ 1.211 +static const char * const LANGUAGES_3[] = { 1.212 + "aar", "abk", "ace", "ach", "ada", "ady", "ave", "afr", 1.213 + "afa", "afh", "agq", "ain", "aka", "akk", "ale", "alg", 1.214 + "alt", "amh", "arg", "ang", "anp", "apa", "ara", "arc", 1.215 + "arn", "arp", "art", "arw", "asm", "asa", "ast", "ath", 1.216 + "aus", "ava", "awa", "aym", "aze", 1.217 + "bak", "bad", "bai", "bal", "ban", "bas", "bat", "bax", 1.218 + "bbj", "bel", "bej", "bem", "ber", "bez", "bfd", "bul", 1.219 + "bih", "bho", "bis", "bik", "bin", "bkm", "bla", "bam", 1.220 + "ben", "bnt", "bod", "bre", "bra", "brx", "bos", "bss", 1.221 + "btk", "bua", "bug", "bum", "byn", "byv", 1.222 + "cat", "cad", "cai", "car", "cau", "cay", "cch", "che", 1.223 + "ceb", "cel", "cgg", "cha", "chb", "chg", "chk", "chm", 1.224 + "chn", "cho", "chp", "chr", "chy", "ckb", "cmc", "cos", 1.225 + "cop", "cpe", "cpf", "cpp", "cre", "crh", "crp", "ces", 1.226 + "csb", "chu", "cus", "chv", "cym", 1.227 + "dan", "dak", "dar", "dav", "day", "deu", "del", "den", 1.228 + "dgr", "din", "dje", "doi", "dra", "dsb", "dua", "dum", 1.229 + "div", "dyo", "dyu", "dzo", "dzg", 1.230 + "ebu", "ewe", "efi", "egy", "eka", "ell", "elx", "eng", 1.231 + "enm", "epo", "spa", "est", "eus", "ewo", 1.232 + "fas", "fan", "fat", "ful", "fin", "fil", "fiu", "fij", 1.233 + "fao", "fon", "fra", "frm", "fro", "frr", "frs", "fur", 1.234 + "fry", 1.235 + "gle", "gaa", "gay", "gba", "gla", "gem", "gez", "gil", 1.236 + "glg", "gmh", "grn", "goh", "gon", "gor", "got", "grb", 1.237 + "grc", "gsw", "guj", "guz", "glv", "gwi", 1.238 + "hau", "hai", "haw", "heb", "hin", "hil", "him", "hit", 1.239 + "hmn", "hmo", "hrv", "hsb", "hat", "hun", "hup", "hye", 1.240 + "her", 1.241 + "ina", "iba", "ibb", "ind", "ile", "ibo", "iii", "ijo", 1.242 + "ipk", "ilo", "inc", "ine", "inh", "ido", "ira", "iro", 1.243 + "isl", "ita", "iku", 1.244 + "jpn", "jbo", "jgo", "jmc", "jpr", "jrb", "jav", 1.245 + "kat", "kaa", "kab", "kac", "kaj", "kam", "kar", "kaw", 1.246 + "kbd", "kbl", "kcg", "kde", "kea", "kfo", "kon", "kha", 1.247 + "khi", "kho", "khq", "kik", "kua", "kaz", "kkj", "kal", 1.248 + "kln", "khm", "kmb", "kan", "kor", "kok", "kos", "kpe", 1.249 + "kau", "krc", "krl", "kro", "kru", "kas", "ksb", "ksf", 1.250 + "ksh", "kur", "kum", "kut", "kom", "cor", "kir", 1.251 + "lat", "lad", "lag", "lah", "lam", "ltz", "lez", "lug", 1.252 + "lim", "lkt", "lin", "lao", "lol", "loz", "lit", "lub", 1.253 + "lua", "lui", "lun", "luo", "lus", "luy", "lav", 1.254 + "mad", "maf", "mag", "mai", "mak", "man", "map", "mas", 1.255 + "mde", "mdf", "mdr", "men", "mer", "mfe", "mlg", "mga", 1.256 + "mgh", "mgo", "mah", "mri", "mic", "min", "mis", "mkd", 1.257 + "mkh", "mal", "mon", "mnc", "mni", "mno", "mol", "moh", 1.258 + "mos", "mar", "msa", "mlt", "mua", "mul", "mun", "mus", 1.259 + "mwl", "mwr", "mya", "mye", "myn", "myv", 1.260 + "nau", "nah", "nai", "nap", "naq", "nob", "nde", "nds", 1.261 + "nep", "new", "ndo", "nia", "nic", "niu", "nld", "nmg", 1.262 + "nno", "nnh", "nor", "nog", "non", "nqo", "nbl", "nso", 1.263 + "nub", "nus", "nav", "nwc", "nya", "nym", "nyn", "nyo", 1.264 + "nzi", 1.265 + "oci", "oji", "orm", "ori", "oss", "osa", "ota", "oto", 1.266 + "pan", "paa", "pag", "pal", "pam", "pap", "pau", "peo", 1.267 + "phi", "phn", "pli", "pol", "pon", "pra", "pro", "pus", 1.268 + "por", 1.269 + "que", 1.270 + "raj", "rap", "rar", "roh", "run", "ron", "roa", "rof", 1.271 + "rom", "rus", "rup", "kin", "rwk", 1.272 + "san", "sad", "sah", "sai", "sal", "sam", "saq", "sas", 1.273 + "sat", "sba", "sbp", "srd", "scn", "sco", "snd", "sme", 1.274 + "see", "seh", "sel", "sem", "ses", "sag", "sga", "sgn", 1.275 + "shi", "shn", "shu", "sin", "sid", "sio", "sit", 1.276 + "slk", "slv", "sla", "smo", "sma", "smi", "smj", "smn", 1.277 + "sms", "sna", "snk", "som", "sog", "son", "sqi", "srp", 1.278 + "srn", "srr", "ssw", "ssa", "ssy", "sot", "sun", "suk", 1.279 + "sus", "sux", "swe", "swa", "swb", "swc", "syc", "syr", 1.280 + "tam", "tai", "tel", "tem", "teo", "ter", "tet", "tgk", 1.281 + "tha", "tir", "tig", "tiv", "tuk", "tkl", "tgl", "tlh", 1.282 + "tli", "tmh", "tsn", "ton", "tog", "tpi", "tur", "trv", 1.283 + "tso", "tsi", "tat", "tum", "tup", "tut", "tvl", "twi", 1.284 + "twq", "tah", "tyv", "tzm", 1.285 + "udm", "uig", "uga", "ukr", "umb", "und", "urd", "uzb", 1.286 + "vai", "ven", "vie", "vol", "vot", "vun", 1.287 + "wln", "wae", "wak", "wal", "war", "was", "wen", "wol", 1.288 + "xal", "xho", "xog", 1.289 + "yao", "yap", "yav", "ybb", "yid", "yor", "ypk", "yue", 1.290 + "zha", "zap", "zbl", "zen", "zgh", "zho", "znd", "zul", 1.291 + "zun", "zxx", "zza", 1.292 +NULL, 1.293 +/* "in", "iw", "ji", "jw", "sh", */ 1.294 + "ind", "heb", "yid", "jaw", "srp", 1.295 +NULL 1.296 +}; 1.297 + 1.298 +/** 1.299 + * Table of 2-letter country codes. 1.300 + * 1.301 + * This list must be in sorted order. This list is returned directly 1.302 + * to the user by some API. 1.303 + * 1.304 + * This list must be kept in sync with COUNTRIES_3, with corresponding 1.305 + * entries matched. 1.306 + * 1.307 + * This table should be terminated with a NULL entry, followed by a 1.308 + * second list, and another NULL entry. The first list is visible to 1.309 + * user code when this array is returned by API. The second list 1.310 + * contains codes we support, but do not expose through user API. 1.311 + * 1.312 + * Notes: 1.313 + * 1.314 + * ZR(ZAR) is now CD(COD) and FX(FXX) is PS(PSE) as per 1.315 + * http://www.evertype.com/standards/iso3166/iso3166-1-en.html added 1.316 + * new codes keeping the old ones for compatibility updated to include 1.317 + * 1999/12/03 revisions *CWB* 1.318 + * 1.319 + * RO(ROM) is now RO(ROU) according to 1.320 + * http://www.iso.org/iso/en/prods-services/iso3166ma/03updates-on-iso-3166/nlv3e-rou.html 1.321 + */ 1.322 +static const char * const COUNTRIES[] = { 1.323 + "AD", "AE", "AF", "AG", "AI", "AL", "AM", 1.324 + "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", 1.325 + "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", 1.326 + "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BV", 1.327 + "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", 1.328 + "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", 1.329 + "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK", 1.330 + "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", 1.331 + "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", 1.332 + "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", 1.333 + "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", 1.334 + "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", 1.335 + "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", 1.336 + "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", 1.337 + "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", 1.338 + "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", 1.339 + "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", 1.340 + "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", 1.341 + "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", 1.342 + "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", 1.343 + "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", 1.344 + "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", 1.345 + "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", 1.346 + "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", 1.347 + "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", 1.348 + "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", 1.349 + "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", 1.350 + "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", 1.351 + "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", 1.352 + "WS", "YE", "YT", "ZA", "ZM", "ZW", 1.353 +NULL, 1.354 + "AN", "BU", "CS", "FX", "RO", "SU", "TP", "YD", "YU", "ZR", /* obsolete country codes */ 1.355 +NULL 1.356 +}; 1.357 + 1.358 +static const char* const DEPRECATED_COUNTRIES[] = { 1.359 + "AN", "BU", "CS", "DD", "DY", "FX", "HV", "NH", "RH", "SU", "TP", "UK", "VD", "YD", "YU", "ZR", NULL, NULL /* deprecated country list */ 1.360 +}; 1.361 +static const char* const REPLACEMENT_COUNTRIES[] = { 1.362 +/* "AN", "BU", "CS", "DD", "DY", "FX", "HV", "NH", "RH", "SU", "TP", "UK", "VD", "YD", "YU", "ZR" */ 1.363 + "CW", "MM", "RS", "DE", "BJ", "FR", "BF", "VU", "ZW", "RU", "TL", "GB", "VN", "YE", "RS", "CD", NULL, NULL /* replacement country codes */ 1.364 +}; 1.365 + 1.366 +/** 1.367 + * Table of 3-letter country codes. 1.368 + * 1.369 + * This is a lookup table used to convert 3-letter country codes to 1.370 + * their 2-letter equivalent. It must be kept in sync with COUNTRIES. 1.371 + * For all valid i, COUNTRIES[i] must refer to the same country as 1.372 + * COUNTRIES_3[i]. The commented-out lines are copied from COUNTRIES 1.373 + * to make eyeballing this baby easier. 1.374 + * 1.375 + * This table should be terminated with a NULL entry, followed by a 1.376 + * second list, and another NULL entry. The two lists correspond to 1.377 + * the two lists in COUNTRIES. 1.378 + */ 1.379 +static const char * const COUNTRIES_3[] = { 1.380 +/* "AD", "AE", "AF", "AG", "AI", "AL", "AM", */ 1.381 + "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM", 1.382 +/* "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", */ 1.383 + "AGO", "ATA", "ARG", "ASM", "AUT", "AUS", "ABW", "ALA", "AZE", 1.384 +/* "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", */ 1.385 + "BIH", "BRB", "BGD", "BEL", "BFA", "BGR", "BHR", "BDI", 1.386 +/* "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BV", */ 1.387 + "BEN", "BLM", "BMU", "BRN", "BOL", "BES", "BRA", "BHS", "BTN", "BVT", 1.388 +/* "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", */ 1.389 + "BWA", "BLR", "BLZ", "CAN", "CCK", "COD", "CAF", "COG", 1.390 +/* "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", */ 1.391 + "CHE", "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CRI", 1.392 +/* "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK", */ 1.393 + "CUB", "CPV", "CUW", "CXR", "CYP", "CZE", "DEU", "DJI", "DNK", 1.394 +/* "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", */ 1.395 + "DMA", "DOM", "DZA", "ECU", "EST", "EGY", "ESH", "ERI", 1.396 +/* "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", */ 1.397 + "ESP", "ETH", "FIN", "FJI", "FLK", "FSM", "FRO", "FRA", 1.398 +/* "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", */ 1.399 + "GAB", "GBR", "GRD", "GEO", "GUF", "GGY", "GHA", "GIB", "GRL", 1.400 +/* "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", */ 1.401 + "GMB", "GIN", "GLP", "GNQ", "GRC", "SGS", "GTM", "GUM", 1.402 +/* "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", */ 1.403 + "GNB", "GUY", "HKG", "HMD", "HND", "HRV", "HTI", "HUN", 1.404 +/* "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS" */ 1.405 + "IDN", "IRL", "ISR", "IMN", "IND", "IOT", "IRQ", "IRN", "ISL", 1.406 +/* "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", */ 1.407 + "ITA", "JEY", "JAM", "JOR", "JPN", "KEN", "KGZ", "KHM", "KIR", 1.408 +/* "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", */ 1.409 + "COM", "KNA", "PRK", "KOR", "KWT", "CYM", "KAZ", "LAO", 1.410 +/* "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", */ 1.411 + "LBN", "LCA", "LIE", "LKA", "LBR", "LSO", "LTU", "LUX", 1.412 +/* "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", */ 1.413 + "LVA", "LBY", "MAR", "MCO", "MDA", "MNE", "MAF", "MDG", "MHL", "MKD", 1.414 +/* "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", */ 1.415 + "MLI", "MMR", "MNG", "MAC", "MNP", "MTQ", "MRT", "MSR", 1.416 +/* "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", */ 1.417 + "MLT", "MUS", "MDV", "MWI", "MEX", "MYS", "MOZ", "NAM", 1.418 +/* "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", */ 1.419 + "NCL", "NER", "NFK", "NGA", "NIC", "NLD", "NOR", "NPL", 1.420 +/* "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", */ 1.421 + "NRU", "NIU", "NZL", "OMN", "PAN", "PER", "PYF", "PNG", 1.422 +/* "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", */ 1.423 + "PHL", "PAK", "POL", "SPM", "PCN", "PRI", "PSE", "PRT", 1.424 +/* "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", */ 1.425 + "PLW", "PRY", "QAT", "REU", "ROU", "SRB", "RUS", "RWA", "SAU", 1.426 +/* "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", */ 1.427 + "SLB", "SYC", "SDN", "SWE", "SGP", "SHN", "SVN", "SJM", 1.428 +/* "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", */ 1.429 + "SVK", "SLE", "SMR", "SEN", "SOM", "SUR", "SSD", "STP", "SLV", 1.430 +/* "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", */ 1.431 + "SXM", "SYR", "SWZ", "TCA", "TCD", "ATF", "TGO", "THA", "TJK", 1.432 +/* "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", */ 1.433 + "TKL", "TLS", "TKM", "TUN", "TON", "TUR", "TTO", "TUV", 1.434 +/* "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", */ 1.435 + "TWN", "TZA", "UKR", "UGA", "UMI", "USA", "URY", "UZB", 1.436 +/* "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", */ 1.437 + "VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT", "WLF", 1.438 +/* "WS", "YE", "YT", "ZA", "ZM", "ZW", */ 1.439 + "WSM", "YEM", "MYT", "ZAF", "ZMB", "ZWE", 1.440 +NULL, 1.441 +/* "AN", "BU", "CS", "FX", "RO", "SU", "TP", "YD", "YU", "ZR" */ 1.442 + "ANT", "BUR", "SCG", "FXX", "ROM", "SUN", "TMP", "YMD", "YUG", "ZAR", 1.443 +NULL 1.444 +}; 1.445 + 1.446 +typedef struct CanonicalizationMap { 1.447 + const char *id; /* input ID */ 1.448 + const char *canonicalID; /* canonicalized output ID */ 1.449 + const char *keyword; /* keyword, or NULL if none */ 1.450 + const char *value; /* keyword value, or NULL if kw==NULL */ 1.451 +} CanonicalizationMap; 1.452 + 1.453 +/** 1.454 + * A map to canonicalize locale IDs. This handles a variety of 1.455 + * different semantic kinds of transformations. 1.456 + */ 1.457 +static const CanonicalizationMap CANONICALIZE_MAP[] = { 1.458 + { "", "en_US_POSIX", NULL, NULL }, /* .NET name */ 1.459 + { "c", "en_US_POSIX", NULL, NULL }, /* POSIX name */ 1.460 + { "posix", "en_US_POSIX", NULL, NULL }, /* POSIX name (alias of C) */ 1.461 + { "art_LOJBAN", "jbo", NULL, NULL }, /* registered name */ 1.462 + { "az_AZ_CYRL", "az_Cyrl_AZ", NULL, NULL }, /* .NET name */ 1.463 + { "az_AZ_LATN", "az_Latn_AZ", NULL, NULL }, /* .NET name */ 1.464 + { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" }, 1.465 + { "de__PHONEBOOK", "de", "collation", "phonebook" }, /* Old ICU name */ 1.466 + { "de_AT_PREEURO", "de_AT", "currency", "ATS" }, 1.467 + { "de_DE_PREEURO", "de_DE", "currency", "DEM" }, 1.468 + { "de_LU_PREEURO", "de_LU", "currency", "LUF" }, 1.469 + { "el_GR_PREEURO", "el_GR", "currency", "GRD" }, 1.470 + { "en_BE_PREEURO", "en_BE", "currency", "BEF" }, 1.471 + { "en_IE_PREEURO", "en_IE", "currency", "IEP" }, 1.472 + { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */ 1.473 + { "es_ES_PREEURO", "es_ES", "currency", "ESP" }, 1.474 + { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" }, 1.475 + { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" }, 1.476 + { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" }, 1.477 + { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" }, 1.478 + { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" }, 1.479 + { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" }, 1.480 + { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" }, 1.481 + { "hi__DIRECT", "hi", "collation", "direct" }, /* Old ICU name */ 1.482 + { "it_IT_PREEURO", "it_IT", "currency", "ITL" }, 1.483 + { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" }, /* Old ICU name */ 1.484 + { "nb_NO_NY", "nn_NO", NULL, NULL }, /* "markus said this was ok" :-) */ 1.485 + { "nl_BE_PREEURO", "nl_BE", "currency", "BEF" }, 1.486 + { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" }, 1.487 + { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" }, 1.488 + { "sr_SP_CYRL", "sr_Cyrl_RS", NULL, NULL }, /* .NET name */ 1.489 + { "sr_SP_LATN", "sr_Latn_RS", NULL, NULL }, /* .NET name */ 1.490 + { "sr_YU_CYRILLIC", "sr_Cyrl_RS", NULL, NULL }, /* Linux name */ 1.491 + { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */ 1.492 + { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", NULL, NULL }, /* Linux name */ 1.493 + { "uz_UZ_CYRL", "uz_Cyrl_UZ", NULL, NULL }, /* .NET name */ 1.494 + { "uz_UZ_LATN", "uz_Latn_UZ", NULL, NULL }, /* .NET name */ 1.495 + { "zh_CHS", "zh_Hans", NULL, NULL }, /* .NET name */ 1.496 + { "zh_CHT", "zh_Hant", NULL, NULL }, /* .NET name */ 1.497 + { "zh_GAN", "gan", NULL, NULL }, /* registered name */ 1.498 + { "zh_GUOYU", "zh", NULL, NULL }, /* registered name */ 1.499 + { "zh_HAKKA", "hak", NULL, NULL }, /* registered name */ 1.500 + { "zh_MIN_NAN", "nan", NULL, NULL }, /* registered name */ 1.501 + { "zh_WUU", "wuu", NULL, NULL }, /* registered name */ 1.502 + { "zh_XIANG", "hsn", NULL, NULL }, /* registered name */ 1.503 + { "zh_YUE", "yue", NULL, NULL }, /* registered name */ 1.504 +}; 1.505 + 1.506 +typedef struct VariantMap { 1.507 + const char *variant; /* input ID */ 1.508 + const char *keyword; /* keyword, or NULL if none */ 1.509 + const char *value; /* keyword value, or NULL if kw==NULL */ 1.510 +} VariantMap; 1.511 + 1.512 +static const VariantMap VARIANT_MAP[] = { 1.513 + { "EURO", "currency", "EUR" }, 1.514 + { "PINYIN", "collation", "pinyin" }, /* Solaris variant */ 1.515 + { "STROKE", "collation", "stroke" } /* Solaris variant */ 1.516 +}; 1.517 + 1.518 +/* ### BCP47 Conversion *******************************************/ 1.519 +/* Test if the locale id has BCP47 u extension and does not have '@' */ 1.520 +#define _hasBCP47Extension(id) (id && uprv_strstr(id, "@") == NULL && getShortestSubtagLength(localeID) == 1) 1.521 +/* Converts the BCP47 id to Unicode id. Does nothing to id if conversion fails */ 1.522 +#define _ConvertBCP47(finalID, id, buffer, length,err) \ 1.523 + if (uloc_forLanguageTag(id, buffer, length, NULL, err) <= 0 || U_FAILURE(*err)) { \ 1.524 + finalID=id; \ 1.525 + } else { \ 1.526 + finalID=buffer; \ 1.527 + } 1.528 +/* Gets the size of the shortest subtag in the given localeID. */ 1.529 +static int32_t getShortestSubtagLength(const char *localeID) { 1.530 + int32_t localeIDLength = uprv_strlen(localeID); 1.531 + int32_t length = localeIDLength; 1.532 + int32_t tmpLength = 0; 1.533 + int32_t i; 1.534 + UBool reset = TRUE; 1.535 + 1.536 + for (i = 0; i < localeIDLength; i++) { 1.537 + if (localeID[i] != '_' && localeID[i] != '-') { 1.538 + if (reset) { 1.539 + tmpLength = 0; 1.540 + reset = FALSE; 1.541 + } 1.542 + tmpLength++; 1.543 + } else { 1.544 + if (tmpLength != 0 && tmpLength < length) { 1.545 + length = tmpLength; 1.546 + } 1.547 + reset = TRUE; 1.548 + } 1.549 + } 1.550 + 1.551 + return length; 1.552 +} 1.553 + 1.554 +/* ### Keywords **************************************************/ 1.555 + 1.556 +#define ULOC_KEYWORD_BUFFER_LEN 25 1.557 +#define ULOC_MAX_NO_KEYWORDS 25 1.558 + 1.559 +U_CAPI const char * U_EXPORT2 1.560 +locale_getKeywordsStart(const char *localeID) { 1.561 + const char *result = NULL; 1.562 + if((result = uprv_strchr(localeID, '@')) != NULL) { 1.563 + return result; 1.564 + } 1.565 +#if (U_CHARSET_FAMILY == U_EBCDIC_FAMILY) 1.566 + else { 1.567 + /* We do this because the @ sign is variant, and the @ sign used on one 1.568 + EBCDIC machine won't be compiled the same way on other EBCDIC based 1.569 + machines. */ 1.570 + static const uint8_t ebcdicSigns[] = { 0x7C, 0x44, 0x66, 0x80, 0xAC, 0xAE, 0xAF, 0xB5, 0xEC, 0xEF, 0x00 }; 1.571 + const uint8_t *charToFind = ebcdicSigns; 1.572 + while(*charToFind) { 1.573 + if((result = uprv_strchr(localeID, *charToFind)) != NULL) { 1.574 + return result; 1.575 + } 1.576 + charToFind++; 1.577 + } 1.578 + } 1.579 +#endif 1.580 + return NULL; 1.581 +} 1.582 + 1.583 +/** 1.584 + * @param buf buffer of size [ULOC_KEYWORD_BUFFER_LEN] 1.585 + * @param keywordName incoming name to be canonicalized 1.586 + * @param status return status (keyword too long) 1.587 + * @return length of the keyword name 1.588 + */ 1.589 +static int32_t locale_canonKeywordName(char *buf, const char *keywordName, UErrorCode *status) 1.590 +{ 1.591 + int32_t i; 1.592 + int32_t keywordNameLen = (int32_t)uprv_strlen(keywordName); 1.593 + 1.594 + if(keywordNameLen >= ULOC_KEYWORD_BUFFER_LEN) { 1.595 + /* keyword name too long for internal buffer */ 1.596 + *status = U_INTERNAL_PROGRAM_ERROR; 1.597 + return 0; 1.598 + } 1.599 + 1.600 + /* normalize the keyword name */ 1.601 + for(i = 0; i < keywordNameLen; i++) { 1.602 + buf[i] = uprv_tolower(keywordName[i]); 1.603 + } 1.604 + buf[i] = 0; 1.605 + 1.606 + return keywordNameLen; 1.607 +} 1.608 + 1.609 +typedef struct { 1.610 + char keyword[ULOC_KEYWORD_BUFFER_LEN]; 1.611 + int32_t keywordLen; 1.612 + const char *valueStart; 1.613 + int32_t valueLen; 1.614 +} KeywordStruct; 1.615 + 1.616 +static int32_t U_CALLCONV 1.617 +compareKeywordStructs(const void * /*context*/, const void *left, const void *right) { 1.618 + const char* leftString = ((const KeywordStruct *)left)->keyword; 1.619 + const char* rightString = ((const KeywordStruct *)right)->keyword; 1.620 + return uprv_strcmp(leftString, rightString); 1.621 +} 1.622 + 1.623 +/** 1.624 + * Both addKeyword and addValue must already be in canonical form. 1.625 + * Either both addKeyword and addValue are NULL, or neither is NULL. 1.626 + * If they are not NULL they must be zero terminated. 1.627 + * If addKeyword is not NULL is must have length small enough to fit in KeywordStruct.keyword. 1.628 + */ 1.629 +static int32_t 1.630 +_getKeywords(const char *localeID, 1.631 + char prev, 1.632 + char *keywords, int32_t keywordCapacity, 1.633 + char *values, int32_t valuesCapacity, int32_t *valLen, 1.634 + UBool valuesToo, 1.635 + const char* addKeyword, 1.636 + const char* addValue, 1.637 + UErrorCode *status) 1.638 +{ 1.639 + KeywordStruct keywordList[ULOC_MAX_NO_KEYWORDS]; 1.640 + 1.641 + int32_t maxKeywords = ULOC_MAX_NO_KEYWORDS; 1.642 + int32_t numKeywords = 0; 1.643 + const char* pos = localeID; 1.644 + const char* equalSign = NULL; 1.645 + const char* semicolon = NULL; 1.646 + int32_t i = 0, j, n; 1.647 + int32_t keywordsLen = 0; 1.648 + int32_t valuesLen = 0; 1.649 + 1.650 + if(prev == '@') { /* start of keyword definition */ 1.651 + /* we will grab pairs, trim spaces, lowercase keywords, sort and return */ 1.652 + do { 1.653 + UBool duplicate = FALSE; 1.654 + /* skip leading spaces */ 1.655 + while(*pos == ' ') { 1.656 + pos++; 1.657 + } 1.658 + if (!*pos) { /* handle trailing "; " */ 1.659 + break; 1.660 + } 1.661 + if(numKeywords == maxKeywords) { 1.662 + *status = U_INTERNAL_PROGRAM_ERROR; 1.663 + return 0; 1.664 + } 1.665 + equalSign = uprv_strchr(pos, '='); 1.666 + semicolon = uprv_strchr(pos, ';'); 1.667 + /* lack of '=' [foo@currency] is illegal */ 1.668 + /* ';' before '=' [foo@currency;collation=pinyin] is illegal */ 1.669 + if(!equalSign || (semicolon && semicolon<equalSign)) { 1.670 + *status = U_INVALID_FORMAT_ERROR; 1.671 + return 0; 1.672 + } 1.673 + /* need to normalize both keyword and keyword name */ 1.674 + if(equalSign - pos >= ULOC_KEYWORD_BUFFER_LEN) { 1.675 + /* keyword name too long for internal buffer */ 1.676 + *status = U_INTERNAL_PROGRAM_ERROR; 1.677 + return 0; 1.678 + } 1.679 + for(i = 0, n = 0; i < equalSign - pos; ++i) { 1.680 + if (pos[i] != ' ') { 1.681 + keywordList[numKeywords].keyword[n++] = uprv_tolower(pos[i]); 1.682 + } 1.683 + } 1.684 + 1.685 + /* zero-length keyword is an error. */ 1.686 + if (n == 0) { 1.687 + *status = U_INVALID_FORMAT_ERROR; 1.688 + return 0; 1.689 + } 1.690 + 1.691 + keywordList[numKeywords].keyword[n] = 0; 1.692 + keywordList[numKeywords].keywordLen = n; 1.693 + /* now grab the value part. First we skip the '=' */ 1.694 + equalSign++; 1.695 + /* then we leading spaces */ 1.696 + while(*equalSign == ' ') { 1.697 + equalSign++; 1.698 + } 1.699 + 1.700 + /* Premature end or zero-length value */ 1.701 + if (!equalSign || equalSign == semicolon) { 1.702 + *status = U_INVALID_FORMAT_ERROR; 1.703 + return 0; 1.704 + } 1.705 + 1.706 + keywordList[numKeywords].valueStart = equalSign; 1.707 + 1.708 + pos = semicolon; 1.709 + i = 0; 1.710 + if(pos) { 1.711 + while(*(pos - i - 1) == ' ') { 1.712 + i++; 1.713 + } 1.714 + keywordList[numKeywords].valueLen = (int32_t)(pos - equalSign - i); 1.715 + pos++; 1.716 + } else { 1.717 + i = (int32_t)uprv_strlen(equalSign); 1.718 + while(i && equalSign[i-1] == ' ') { 1.719 + i--; 1.720 + } 1.721 + keywordList[numKeywords].valueLen = i; 1.722 + } 1.723 + /* If this is a duplicate keyword, then ignore it */ 1.724 + for (j=0; j<numKeywords; ++j) { 1.725 + if (uprv_strcmp(keywordList[j].keyword, keywordList[numKeywords].keyword) == 0) { 1.726 + duplicate = TRUE; 1.727 + break; 1.728 + } 1.729 + } 1.730 + if (!duplicate) { 1.731 + ++numKeywords; 1.732 + } 1.733 + } while(pos); 1.734 + 1.735 + /* Handle addKeyword/addValue. */ 1.736 + if (addKeyword != NULL) { 1.737 + UBool duplicate = FALSE; 1.738 + U_ASSERT(addValue != NULL); 1.739 + /* Search for duplicate; if found, do nothing. Explicit keyword 1.740 + overrides addKeyword. */ 1.741 + for (j=0; j<numKeywords; ++j) { 1.742 + if (uprv_strcmp(keywordList[j].keyword, addKeyword) == 0) { 1.743 + duplicate = TRUE; 1.744 + break; 1.745 + } 1.746 + } 1.747 + if (!duplicate) { 1.748 + if (numKeywords == maxKeywords) { 1.749 + *status = U_INTERNAL_PROGRAM_ERROR; 1.750 + return 0; 1.751 + } 1.752 + uprv_strcpy(keywordList[numKeywords].keyword, addKeyword); 1.753 + keywordList[numKeywords].keywordLen = (int32_t)uprv_strlen(addKeyword); 1.754 + keywordList[numKeywords].valueStart = addValue; 1.755 + keywordList[numKeywords].valueLen = (int32_t)uprv_strlen(addValue); 1.756 + ++numKeywords; 1.757 + } 1.758 + } else { 1.759 + U_ASSERT(addValue == NULL); 1.760 + } 1.761 + 1.762 + /* now we have a list of keywords */ 1.763 + /* we need to sort it */ 1.764 + uprv_sortArray(keywordList, numKeywords, sizeof(KeywordStruct), compareKeywordStructs, NULL, FALSE, status); 1.765 + 1.766 + /* Now construct the keyword part */ 1.767 + for(i = 0; i < numKeywords; i++) { 1.768 + if(keywordsLen + keywordList[i].keywordLen + 1< keywordCapacity) { 1.769 + uprv_strcpy(keywords+keywordsLen, keywordList[i].keyword); 1.770 + if(valuesToo) { 1.771 + keywords[keywordsLen + keywordList[i].keywordLen] = '='; 1.772 + } else { 1.773 + keywords[keywordsLen + keywordList[i].keywordLen] = 0; 1.774 + } 1.775 + } 1.776 + keywordsLen += keywordList[i].keywordLen + 1; 1.777 + if(valuesToo) { 1.778 + if(keywordsLen + keywordList[i].valueLen < keywordCapacity) { 1.779 + uprv_strncpy(keywords+keywordsLen, keywordList[i].valueStart, keywordList[i].valueLen); 1.780 + } 1.781 + keywordsLen += keywordList[i].valueLen; 1.782 + 1.783 + if(i < numKeywords - 1) { 1.784 + if(keywordsLen < keywordCapacity) { 1.785 + keywords[keywordsLen] = ';'; 1.786 + } 1.787 + keywordsLen++; 1.788 + } 1.789 + } 1.790 + if(values) { 1.791 + if(valuesLen + keywordList[i].valueLen + 1< valuesCapacity) { 1.792 + uprv_strcpy(values+valuesLen, keywordList[i].valueStart); 1.793 + values[valuesLen + keywordList[i].valueLen] = 0; 1.794 + } 1.795 + valuesLen += keywordList[i].valueLen + 1; 1.796 + } 1.797 + } 1.798 + if(values) { 1.799 + values[valuesLen] = 0; 1.800 + if(valLen) { 1.801 + *valLen = valuesLen; 1.802 + } 1.803 + } 1.804 + return u_terminateChars(keywords, keywordCapacity, keywordsLen, status); 1.805 + } else { 1.806 + return 0; 1.807 + } 1.808 +} 1.809 + 1.810 +U_CFUNC int32_t 1.811 +locale_getKeywords(const char *localeID, 1.812 + char prev, 1.813 + char *keywords, int32_t keywordCapacity, 1.814 + char *values, int32_t valuesCapacity, int32_t *valLen, 1.815 + UBool valuesToo, 1.816 + UErrorCode *status) { 1.817 + return _getKeywords(localeID, prev, keywords, keywordCapacity, 1.818 + values, valuesCapacity, valLen, valuesToo, 1.819 + NULL, NULL, status); 1.820 +} 1.821 + 1.822 +U_CAPI int32_t U_EXPORT2 1.823 +uloc_getKeywordValue(const char* localeID, 1.824 + const char* keywordName, 1.825 + char* buffer, int32_t bufferCapacity, 1.826 + UErrorCode* status) 1.827 +{ 1.828 + const char* startSearchHere = NULL; 1.829 + const char* nextSeparator = NULL; 1.830 + char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; 1.831 + char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; 1.832 + int32_t i = 0; 1.833 + int32_t result = 0; 1.834 + 1.835 + if(status && U_SUCCESS(*status) && localeID) { 1.836 + char tempBuffer[ULOC_FULLNAME_CAPACITY]; 1.837 + const char* tmpLocaleID; 1.838 + 1.839 + if (_hasBCP47Extension(localeID)) { 1.840 + _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), status); 1.841 + } else { 1.842 + tmpLocaleID=localeID; 1.843 + } 1.844 + 1.845 + startSearchHere = uprv_strchr(tmpLocaleID, '@'); /* TODO: REVISIT: shouldn't this be locale_getKeywordsStart ? */ 1.846 + if(startSearchHere == NULL) { 1.847 + /* no keywords, return at once */ 1.848 + return 0; 1.849 + } 1.850 + 1.851 + locale_canonKeywordName(keywordNameBuffer, keywordName, status); 1.852 + if(U_FAILURE(*status)) { 1.853 + return 0; 1.854 + } 1.855 + 1.856 + /* find the first keyword */ 1.857 + while(startSearchHere) { 1.858 + startSearchHere++; 1.859 + /* skip leading spaces (allowed?) */ 1.860 + while(*startSearchHere == ' ') { 1.861 + startSearchHere++; 1.862 + } 1.863 + nextSeparator = uprv_strchr(startSearchHere, '='); 1.864 + /* need to normalize both keyword and keyword name */ 1.865 + if(!nextSeparator) { 1.866 + break; 1.867 + } 1.868 + if(nextSeparator - startSearchHere >= ULOC_KEYWORD_BUFFER_LEN) { 1.869 + /* keyword name too long for internal buffer */ 1.870 + *status = U_INTERNAL_PROGRAM_ERROR; 1.871 + return 0; 1.872 + } 1.873 + for(i = 0; i < nextSeparator - startSearchHere; i++) { 1.874 + localeKeywordNameBuffer[i] = uprv_tolower(startSearchHere[i]); 1.875 + } 1.876 + /* trim trailing spaces */ 1.877 + while(startSearchHere[i-1] == ' ') { 1.878 + i--; 1.879 + U_ASSERT(i>=0); 1.880 + } 1.881 + localeKeywordNameBuffer[i] = 0; 1.882 + 1.883 + startSearchHere = uprv_strchr(nextSeparator, ';'); 1.884 + 1.885 + if(uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer) == 0) { 1.886 + nextSeparator++; 1.887 + while(*nextSeparator == ' ') { 1.888 + nextSeparator++; 1.889 + } 1.890 + /* we actually found the keyword. Copy the value */ 1.891 + if(startSearchHere && startSearchHere - nextSeparator < bufferCapacity) { 1.892 + while(*(startSearchHere-1) == ' ') { 1.893 + startSearchHere--; 1.894 + } 1.895 + uprv_strncpy(buffer, nextSeparator, startSearchHere - nextSeparator); 1.896 + result = u_terminateChars(buffer, bufferCapacity, (int32_t)(startSearchHere - nextSeparator), status); 1.897 + } else if(!startSearchHere && (int32_t)uprv_strlen(nextSeparator) < bufferCapacity) { /* last item in string */ 1.898 + i = (int32_t)uprv_strlen(nextSeparator); 1.899 + while(nextSeparator[i - 1] == ' ') { 1.900 + i--; 1.901 + } 1.902 + uprv_strncpy(buffer, nextSeparator, i); 1.903 + result = u_terminateChars(buffer, bufferCapacity, i, status); 1.904 + } else { 1.905 + /* give a bigger buffer, please */ 1.906 + *status = U_BUFFER_OVERFLOW_ERROR; 1.907 + if(startSearchHere) { 1.908 + result = (int32_t)(startSearchHere - nextSeparator); 1.909 + } else { 1.910 + result = (int32_t)uprv_strlen(nextSeparator); 1.911 + } 1.912 + } 1.913 + return result; 1.914 + } 1.915 + } 1.916 + } 1.917 + return 0; 1.918 +} 1.919 + 1.920 +U_CAPI int32_t U_EXPORT2 1.921 +uloc_setKeywordValue(const char* keywordName, 1.922 + const char* keywordValue, 1.923 + char* buffer, int32_t bufferCapacity, 1.924 + UErrorCode* status) 1.925 +{ 1.926 + /* TODO: sorting. removal. */ 1.927 + int32_t keywordNameLen; 1.928 + int32_t keywordValueLen; 1.929 + int32_t bufLen; 1.930 + int32_t needLen = 0; 1.931 + int32_t foundValueLen; 1.932 + int32_t keywordAtEnd = 0; /* is the keyword at the end of the string? */ 1.933 + char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; 1.934 + char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; 1.935 + int32_t i = 0; 1.936 + int32_t rc; 1.937 + char* nextSeparator = NULL; 1.938 + char* nextEqualsign = NULL; 1.939 + char* startSearchHere = NULL; 1.940 + char* keywordStart = NULL; 1.941 + char *insertHere = NULL; 1.942 + if(U_FAILURE(*status)) { 1.943 + return -1; 1.944 + } 1.945 + if(bufferCapacity>1) { 1.946 + bufLen = (int32_t)uprv_strlen(buffer); 1.947 + } else { 1.948 + *status = U_ILLEGAL_ARGUMENT_ERROR; 1.949 + return 0; 1.950 + } 1.951 + if(bufferCapacity<bufLen) { 1.952 + /* The capacity is less than the length?! Is this NULL terminated? */ 1.953 + *status = U_ILLEGAL_ARGUMENT_ERROR; 1.954 + return 0; 1.955 + } 1.956 + if(keywordValue && !*keywordValue) { 1.957 + keywordValue = NULL; 1.958 + } 1.959 + if(keywordValue) { 1.960 + keywordValueLen = (int32_t)uprv_strlen(keywordValue); 1.961 + } else { 1.962 + keywordValueLen = 0; 1.963 + } 1.964 + keywordNameLen = locale_canonKeywordName(keywordNameBuffer, keywordName, status); 1.965 + if(U_FAILURE(*status)) { 1.966 + return 0; 1.967 + } 1.968 + startSearchHere = (char*)locale_getKeywordsStart(buffer); 1.969 + if(startSearchHere == NULL || (startSearchHere[1]==0)) { 1.970 + if(!keywordValue) { /* no keywords = nothing to remove */ 1.971 + return bufLen; 1.972 + } 1.973 + 1.974 + needLen = bufLen+1+keywordNameLen+1+keywordValueLen; 1.975 + if(startSearchHere) { /* had a single @ */ 1.976 + needLen--; /* already had the @ */ 1.977 + /* startSearchHere points at the @ */ 1.978 + } else { 1.979 + startSearchHere=buffer+bufLen; 1.980 + } 1.981 + if(needLen >= bufferCapacity) { 1.982 + *status = U_BUFFER_OVERFLOW_ERROR; 1.983 + return needLen; /* no change */ 1.984 + } 1.985 + *startSearchHere = '@'; 1.986 + startSearchHere++; 1.987 + uprv_strcpy(startSearchHere, keywordNameBuffer); 1.988 + startSearchHere += keywordNameLen; 1.989 + *startSearchHere = '='; 1.990 + startSearchHere++; 1.991 + uprv_strcpy(startSearchHere, keywordValue); 1.992 + startSearchHere+=keywordValueLen; 1.993 + return needLen; 1.994 + } /* end shortcut - no @ */ 1.995 + 1.996 + keywordStart = startSearchHere; 1.997 + /* search for keyword */ 1.998 + while(keywordStart) { 1.999 + keywordStart++; 1.1000 + /* skip leading spaces (allowed?) */ 1.1001 + while(*keywordStart == ' ') { 1.1002 + keywordStart++; 1.1003 + } 1.1004 + nextEqualsign = uprv_strchr(keywordStart, '='); 1.1005 + /* need to normalize both keyword and keyword name */ 1.1006 + if(!nextEqualsign) { 1.1007 + break; 1.1008 + } 1.1009 + if(nextEqualsign - keywordStart >= ULOC_KEYWORD_BUFFER_LEN) { 1.1010 + /* keyword name too long for internal buffer */ 1.1011 + *status = U_INTERNAL_PROGRAM_ERROR; 1.1012 + return 0; 1.1013 + } 1.1014 + for(i = 0; i < nextEqualsign - keywordStart; i++) { 1.1015 + localeKeywordNameBuffer[i] = uprv_tolower(keywordStart[i]); 1.1016 + } 1.1017 + /* trim trailing spaces */ 1.1018 + while(keywordStart[i-1] == ' ') { 1.1019 + i--; 1.1020 + } 1.1021 + U_ASSERT(i>=0 && i<ULOC_KEYWORD_BUFFER_LEN); 1.1022 + localeKeywordNameBuffer[i] = 0; 1.1023 + 1.1024 + nextSeparator = uprv_strchr(nextEqualsign, ';'); 1.1025 + rc = uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer); 1.1026 + if(rc == 0) { 1.1027 + nextEqualsign++; 1.1028 + while(*nextEqualsign == ' ') { 1.1029 + nextEqualsign++; 1.1030 + } 1.1031 + /* we actually found the keyword. Change the value */ 1.1032 + if (nextSeparator) { 1.1033 + keywordAtEnd = 0; 1.1034 + foundValueLen = (int32_t)(nextSeparator - nextEqualsign); 1.1035 + } else { 1.1036 + keywordAtEnd = 1; 1.1037 + foundValueLen = (int32_t)uprv_strlen(nextEqualsign); 1.1038 + } 1.1039 + if(keywordValue) { /* adding a value - not removing */ 1.1040 + if(foundValueLen == keywordValueLen) { 1.1041 + uprv_strncpy(nextEqualsign, keywordValue, keywordValueLen); 1.1042 + return bufLen; /* no change in size */ 1.1043 + } else if(foundValueLen > keywordValueLen) { 1.1044 + int32_t delta = foundValueLen - keywordValueLen; 1.1045 + if(nextSeparator) { /* RH side */ 1.1046 + uprv_memmove(nextSeparator - delta, nextSeparator, bufLen-(nextSeparator-buffer)); 1.1047 + } 1.1048 + uprv_strncpy(nextEqualsign, keywordValue, keywordValueLen); 1.1049 + bufLen -= delta; 1.1050 + buffer[bufLen]=0; 1.1051 + return bufLen; 1.1052 + } else { /* FVL < KVL */ 1.1053 + int32_t delta = keywordValueLen - foundValueLen; 1.1054 + if((bufLen+delta) >= bufferCapacity) { 1.1055 + *status = U_BUFFER_OVERFLOW_ERROR; 1.1056 + return bufLen+delta; 1.1057 + } 1.1058 + if(nextSeparator) { /* RH side */ 1.1059 + uprv_memmove(nextSeparator+delta,nextSeparator, bufLen-(nextSeparator-buffer)); 1.1060 + } 1.1061 + uprv_strncpy(nextEqualsign, keywordValue, keywordValueLen); 1.1062 + bufLen += delta; 1.1063 + buffer[bufLen]=0; 1.1064 + return bufLen; 1.1065 + } 1.1066 + } else { /* removing a keyword */ 1.1067 + if(keywordAtEnd) { 1.1068 + /* zero out the ';' or '@' just before startSearchhere */ 1.1069 + keywordStart[-1] = 0; 1.1070 + return (int32_t)((keywordStart-buffer)-1); /* (string length without keyword) minus separator */ 1.1071 + } else { 1.1072 + uprv_memmove(keywordStart, nextSeparator+1, bufLen-((nextSeparator+1)-buffer)); 1.1073 + keywordStart[bufLen-((nextSeparator+1)-buffer)]=0; 1.1074 + return (int32_t)(bufLen-((nextSeparator+1)-keywordStart)); 1.1075 + } 1.1076 + } 1.1077 + } else if(rc<0){ /* end match keyword */ 1.1078 + /* could insert at this location. */ 1.1079 + insertHere = keywordStart; 1.1080 + } 1.1081 + keywordStart = nextSeparator; 1.1082 + } /* end loop searching */ 1.1083 + 1.1084 + if(!keywordValue) { 1.1085 + return bufLen; /* removal of non-extant keyword - no change */ 1.1086 + } 1.1087 + 1.1088 + /* we know there is at least one keyword. */ 1.1089 + needLen = bufLen+1+keywordNameLen+1+keywordValueLen; 1.1090 + if(needLen >= bufferCapacity) { 1.1091 + *status = U_BUFFER_OVERFLOW_ERROR; 1.1092 + return needLen; /* no change */ 1.1093 + } 1.1094 + 1.1095 + if(insertHere) { 1.1096 + uprv_memmove(insertHere+(1+keywordNameLen+1+keywordValueLen), insertHere, bufLen-(insertHere-buffer)); 1.1097 + keywordStart = insertHere; 1.1098 + } else { 1.1099 + keywordStart = buffer+bufLen; 1.1100 + *keywordStart = ';'; 1.1101 + keywordStart++; 1.1102 + } 1.1103 + uprv_strncpy(keywordStart, keywordNameBuffer, keywordNameLen); 1.1104 + keywordStart += keywordNameLen; 1.1105 + *keywordStart = '='; 1.1106 + keywordStart++; 1.1107 + uprv_strncpy(keywordStart, keywordValue, keywordValueLen); /* terminates. */ 1.1108 + keywordStart+=keywordValueLen; 1.1109 + if(insertHere) { 1.1110 + *keywordStart = ';'; 1.1111 + keywordStart++; 1.1112 + } 1.1113 + buffer[needLen]=0; 1.1114 + return needLen; 1.1115 +} 1.1116 + 1.1117 +/* ### ID parsing implementation **************************************************/ 1.1118 + 1.1119 +#define _isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I')) 1.1120 + 1.1121 +/*returns TRUE if one of the special prefixes is here (s=string) 1.1122 + 'x-' or 'i-' */ 1.1123 +#define _isIDPrefix(s) (_isPrefixLetter(s[0])&&_isIDSeparator(s[1])) 1.1124 + 1.1125 +/* Dot terminates it because of POSIX form where dot precedes the codepage 1.1126 + * except for variant 1.1127 + */ 1.1128 +#define _isTerminator(a) ((a==0)||(a=='.')||(a=='@')) 1.1129 + 1.1130 +static char* _strnchr(const char* str, int32_t len, char c) { 1.1131 + U_ASSERT(str != 0 && len >= 0); 1.1132 + while (len-- != 0) { 1.1133 + char d = *str; 1.1134 + if (d == c) { 1.1135 + return (char*) str; 1.1136 + } else if (d == 0) { 1.1137 + break; 1.1138 + } 1.1139 + ++str; 1.1140 + } 1.1141 + return NULL; 1.1142 +} 1.1143 + 1.1144 +/** 1.1145 + * Lookup 'key' in the array 'list'. The array 'list' should contain 1.1146 + * a NULL entry, followed by more entries, and a second NULL entry. 1.1147 + * 1.1148 + * The 'list' param should be LANGUAGES, LANGUAGES_3, COUNTRIES, or 1.1149 + * COUNTRIES_3. 1.1150 + */ 1.1151 +static int16_t _findIndex(const char* const* list, const char* key) 1.1152 +{ 1.1153 + const char* const* anchor = list; 1.1154 + int32_t pass = 0; 1.1155 + 1.1156 + /* Make two passes through two NULL-terminated arrays at 'list' */ 1.1157 + while (pass++ < 2) { 1.1158 + while (*list) { 1.1159 + if (uprv_strcmp(key, *list) == 0) { 1.1160 + return (int16_t)(list - anchor); 1.1161 + } 1.1162 + list++; 1.1163 + } 1.1164 + ++list; /* skip final NULL *CWB*/ 1.1165 + } 1.1166 + return -1; 1.1167 +} 1.1168 + 1.1169 +/* count the length of src while copying it to dest; return strlen(src) */ 1.1170 +static inline int32_t 1.1171 +_copyCount(char *dest, int32_t destCapacity, const char *src) { 1.1172 + const char *anchor; 1.1173 + char c; 1.1174 + 1.1175 + anchor=src; 1.1176 + for(;;) { 1.1177 + if((c=*src)==0) { 1.1178 + return (int32_t)(src-anchor); 1.1179 + } 1.1180 + if(destCapacity<=0) { 1.1181 + return (int32_t)((src-anchor)+uprv_strlen(src)); 1.1182 + } 1.1183 + ++src; 1.1184 + *dest++=c; 1.1185 + --destCapacity; 1.1186 + } 1.1187 +} 1.1188 + 1.1189 +U_CFUNC const char* 1.1190 +uloc_getCurrentCountryID(const char* oldID){ 1.1191 + int32_t offset = _findIndex(DEPRECATED_COUNTRIES, oldID); 1.1192 + if (offset >= 0) { 1.1193 + return REPLACEMENT_COUNTRIES[offset]; 1.1194 + } 1.1195 + return oldID; 1.1196 +} 1.1197 +U_CFUNC const char* 1.1198 +uloc_getCurrentLanguageID(const char* oldID){ 1.1199 + int32_t offset = _findIndex(DEPRECATED_LANGUAGES, oldID); 1.1200 + if (offset >= 0) { 1.1201 + return REPLACEMENT_LANGUAGES[offset]; 1.1202 + } 1.1203 + return oldID; 1.1204 +} 1.1205 +/* 1.1206 + * the internal functions _getLanguage(), _getCountry(), _getVariant() 1.1207 + * avoid duplicating code to handle the earlier locale ID pieces 1.1208 + * in the functions for the later ones by 1.1209 + * setting the *pEnd pointer to where they stopped parsing 1.1210 + * 1.1211 + * TODO try to use this in Locale 1.1212 + */ 1.1213 +U_CFUNC int32_t 1.1214 +ulocimp_getLanguage(const char *localeID, 1.1215 + char *language, int32_t languageCapacity, 1.1216 + const char **pEnd) { 1.1217 + int32_t i=0; 1.1218 + int32_t offset; 1.1219 + char lang[4]={ 0, 0, 0, 0 }; /* temporary buffer to hold language code for searching */ 1.1220 + 1.1221 + /* if it starts with i- or x- then copy that prefix */ 1.1222 + if(_isIDPrefix(localeID)) { 1.1223 + if(i<languageCapacity) { 1.1224 + language[i]=(char)uprv_tolower(*localeID); 1.1225 + } 1.1226 + if(i<languageCapacity) { 1.1227 + language[i+1]='-'; 1.1228 + } 1.1229 + i+=2; 1.1230 + localeID+=2; 1.1231 + } 1.1232 + 1.1233 + /* copy the language as far as possible and count its length */ 1.1234 + while(!_isTerminator(*localeID) && !_isIDSeparator(*localeID)) { 1.1235 + if(i<languageCapacity) { 1.1236 + language[i]=(char)uprv_tolower(*localeID); 1.1237 + } 1.1238 + if(i<3) { 1.1239 + U_ASSERT(i>=0); 1.1240 + lang[i]=(char)uprv_tolower(*localeID); 1.1241 + } 1.1242 + i++; 1.1243 + localeID++; 1.1244 + } 1.1245 + 1.1246 + if(i==3) { 1.1247 + /* convert 3 character code to 2 character code if possible *CWB*/ 1.1248 + offset=_findIndex(LANGUAGES_3, lang); 1.1249 + if(offset>=0) { 1.1250 + i=_copyCount(language, languageCapacity, LANGUAGES[offset]); 1.1251 + } 1.1252 + } 1.1253 + 1.1254 + if(pEnd!=NULL) { 1.1255 + *pEnd=localeID; 1.1256 + } 1.1257 + return i; 1.1258 +} 1.1259 + 1.1260 +U_CFUNC int32_t 1.1261 +ulocimp_getScript(const char *localeID, 1.1262 + char *script, int32_t scriptCapacity, 1.1263 + const char **pEnd) 1.1264 +{ 1.1265 + int32_t idLen = 0; 1.1266 + 1.1267 + if (pEnd != NULL) { 1.1268 + *pEnd = localeID; 1.1269 + } 1.1270 + 1.1271 + /* copy the second item as far as possible and count its length */ 1.1272 + while(!_isTerminator(localeID[idLen]) && !_isIDSeparator(localeID[idLen]) 1.1273 + && uprv_isASCIILetter(localeID[idLen])) { 1.1274 + idLen++; 1.1275 + } 1.1276 + 1.1277 + /* If it's exactly 4 characters long, then it's a script and not a country. */ 1.1278 + if (idLen == 4) { 1.1279 + int32_t i; 1.1280 + if (pEnd != NULL) { 1.1281 + *pEnd = localeID+idLen; 1.1282 + } 1.1283 + if(idLen > scriptCapacity) { 1.1284 + idLen = scriptCapacity; 1.1285 + } 1.1286 + if (idLen >= 1) { 1.1287 + script[0]=(char)uprv_toupper(*(localeID++)); 1.1288 + } 1.1289 + for (i = 1; i < idLen; i++) { 1.1290 + script[i]=(char)uprv_tolower(*(localeID++)); 1.1291 + } 1.1292 + } 1.1293 + else { 1.1294 + idLen = 0; 1.1295 + } 1.1296 + return idLen; 1.1297 +} 1.1298 + 1.1299 +U_CFUNC int32_t 1.1300 +ulocimp_getCountry(const char *localeID, 1.1301 + char *country, int32_t countryCapacity, 1.1302 + const char **pEnd) 1.1303 +{ 1.1304 + int32_t idLen=0; 1.1305 + char cnty[ULOC_COUNTRY_CAPACITY]={ 0, 0, 0, 0 }; 1.1306 + int32_t offset; 1.1307 + 1.1308 + /* copy the country as far as possible and count its length */ 1.1309 + while(!_isTerminator(localeID[idLen]) && !_isIDSeparator(localeID[idLen])) { 1.1310 + if(idLen<(ULOC_COUNTRY_CAPACITY-1)) { /*CWB*/ 1.1311 + cnty[idLen]=(char)uprv_toupper(localeID[idLen]); 1.1312 + } 1.1313 + idLen++; 1.1314 + } 1.1315 + 1.1316 + /* the country should be either length 2 or 3 */ 1.1317 + if (idLen == 2 || idLen == 3) { 1.1318 + UBool gotCountry = FALSE; 1.1319 + /* convert 3 character code to 2 character code if possible *CWB*/ 1.1320 + if(idLen==3) { 1.1321 + offset=_findIndex(COUNTRIES_3, cnty); 1.1322 + if(offset>=0) { 1.1323 + idLen=_copyCount(country, countryCapacity, COUNTRIES[offset]); 1.1324 + gotCountry = TRUE; 1.1325 + } 1.1326 + } 1.1327 + if (!gotCountry) { 1.1328 + int32_t i = 0; 1.1329 + for (i = 0; i < idLen; i++) { 1.1330 + if (i < countryCapacity) { 1.1331 + country[i]=(char)uprv_toupper(localeID[i]); 1.1332 + } 1.1333 + } 1.1334 + } 1.1335 + localeID+=idLen; 1.1336 + } else { 1.1337 + idLen = 0; 1.1338 + } 1.1339 + 1.1340 + if(pEnd!=NULL) { 1.1341 + *pEnd=localeID; 1.1342 + } 1.1343 + 1.1344 + return idLen; 1.1345 +} 1.1346 + 1.1347 +/** 1.1348 + * @param needSeparator if true, then add leading '_' if any variants 1.1349 + * are added to 'variant' 1.1350 + */ 1.1351 +static int32_t 1.1352 +_getVariantEx(const char *localeID, 1.1353 + char prev, 1.1354 + char *variant, int32_t variantCapacity, 1.1355 + UBool needSeparator) { 1.1356 + int32_t i=0; 1.1357 + 1.1358 + /* get one or more variant tags and separate them with '_' */ 1.1359 + if(_isIDSeparator(prev)) { 1.1360 + /* get a variant string after a '-' or '_' */ 1.1361 + while(!_isTerminator(*localeID)) { 1.1362 + if (needSeparator) { 1.1363 + if (i<variantCapacity) { 1.1364 + variant[i] = '_'; 1.1365 + } 1.1366 + ++i; 1.1367 + needSeparator = FALSE; 1.1368 + } 1.1369 + if(i<variantCapacity) { 1.1370 + variant[i]=(char)uprv_toupper(*localeID); 1.1371 + if(variant[i]=='-') { 1.1372 + variant[i]='_'; 1.1373 + } 1.1374 + } 1.1375 + i++; 1.1376 + localeID++; 1.1377 + } 1.1378 + } 1.1379 + 1.1380 + /* if there is no variant tag after a '-' or '_' then look for '@' */ 1.1381 + if(i==0) { 1.1382 + if(prev=='@') { 1.1383 + /* keep localeID */ 1.1384 + } else if((localeID=locale_getKeywordsStart(localeID))!=NULL) { 1.1385 + ++localeID; /* point after the '@' */ 1.1386 + } else { 1.1387 + return 0; 1.1388 + } 1.1389 + while(!_isTerminator(*localeID)) { 1.1390 + if (needSeparator) { 1.1391 + if (i<variantCapacity) { 1.1392 + variant[i] = '_'; 1.1393 + } 1.1394 + ++i; 1.1395 + needSeparator = FALSE; 1.1396 + } 1.1397 + if(i<variantCapacity) { 1.1398 + variant[i]=(char)uprv_toupper(*localeID); 1.1399 + if(variant[i]=='-' || variant[i]==',') { 1.1400 + variant[i]='_'; 1.1401 + } 1.1402 + } 1.1403 + i++; 1.1404 + localeID++; 1.1405 + } 1.1406 + } 1.1407 + 1.1408 + return i; 1.1409 +} 1.1410 + 1.1411 +static int32_t 1.1412 +_getVariant(const char *localeID, 1.1413 + char prev, 1.1414 + char *variant, int32_t variantCapacity) { 1.1415 + return _getVariantEx(localeID, prev, variant, variantCapacity, FALSE); 1.1416 +} 1.1417 + 1.1418 +/** 1.1419 + * Delete ALL instances of a variant from the given list of one or 1.1420 + * more variants. Example: "FOO_EURO_BAR_EURO" => "FOO_BAR". 1.1421 + * @param variants the source string of one or more variants, 1.1422 + * separated by '_'. This will be MODIFIED IN PLACE. Not zero 1.1423 + * terminated; if it is, trailing zero will NOT be maintained. 1.1424 + * @param variantsLen length of variants 1.1425 + * @param toDelete variant to delete, without separators, e.g. "EURO" 1.1426 + * or "PREEURO"; not zero terminated 1.1427 + * @param toDeleteLen length of toDelete 1.1428 + * @return number of characters deleted from variants 1.1429 + */ 1.1430 +static int32_t 1.1431 +_deleteVariant(char* variants, int32_t variantsLen, 1.1432 + const char* toDelete, int32_t toDeleteLen) 1.1433 +{ 1.1434 + int32_t delta = 0; /* number of chars deleted */ 1.1435 + for (;;) { 1.1436 + UBool flag = FALSE; 1.1437 + if (variantsLen < toDeleteLen) { 1.1438 + return delta; 1.1439 + } 1.1440 + if (uprv_strncmp(variants, toDelete, toDeleteLen) == 0 && 1.1441 + (variantsLen == toDeleteLen || 1.1442 + (flag=(variants[toDeleteLen] == '_')))) 1.1443 + { 1.1444 + int32_t d = toDeleteLen + (flag?1:0); 1.1445 + variantsLen -= d; 1.1446 + delta += d; 1.1447 + if (variantsLen > 0) { 1.1448 + uprv_memmove(variants, variants+d, variantsLen); 1.1449 + } 1.1450 + } else { 1.1451 + char* p = _strnchr(variants, variantsLen, '_'); 1.1452 + if (p == NULL) { 1.1453 + return delta; 1.1454 + } 1.1455 + ++p; 1.1456 + variantsLen -= (int32_t)(p - variants); 1.1457 + variants = p; 1.1458 + } 1.1459 + } 1.1460 +} 1.1461 + 1.1462 +/* Keyword enumeration */ 1.1463 + 1.1464 +typedef struct UKeywordsContext { 1.1465 + char* keywords; 1.1466 + char* current; 1.1467 +} UKeywordsContext; 1.1468 + 1.1469 +static void U_CALLCONV 1.1470 +uloc_kw_closeKeywords(UEnumeration *enumerator) { 1.1471 + uprv_free(((UKeywordsContext *)enumerator->context)->keywords); 1.1472 + uprv_free(enumerator->context); 1.1473 + uprv_free(enumerator); 1.1474 +} 1.1475 + 1.1476 +static int32_t U_CALLCONV 1.1477 +uloc_kw_countKeywords(UEnumeration *en, UErrorCode * /*status*/) { 1.1478 + char *kw = ((UKeywordsContext *)en->context)->keywords; 1.1479 + int32_t result = 0; 1.1480 + while(*kw) { 1.1481 + result++; 1.1482 + kw += uprv_strlen(kw)+1; 1.1483 + } 1.1484 + return result; 1.1485 +} 1.1486 + 1.1487 +static const char* U_CALLCONV 1.1488 +uloc_kw_nextKeyword(UEnumeration* en, 1.1489 + int32_t* resultLength, 1.1490 + UErrorCode* /*status*/) { 1.1491 + const char* result = ((UKeywordsContext *)en->context)->current; 1.1492 + int32_t len = 0; 1.1493 + if(*result) { 1.1494 + len = (int32_t)uprv_strlen(((UKeywordsContext *)en->context)->current); 1.1495 + ((UKeywordsContext *)en->context)->current += len+1; 1.1496 + } else { 1.1497 + result = NULL; 1.1498 + } 1.1499 + if (resultLength) { 1.1500 + *resultLength = len; 1.1501 + } 1.1502 + return result; 1.1503 +} 1.1504 + 1.1505 +static void U_CALLCONV 1.1506 +uloc_kw_resetKeywords(UEnumeration* en, 1.1507 + UErrorCode* /*status*/) { 1.1508 + ((UKeywordsContext *)en->context)->current = ((UKeywordsContext *)en->context)->keywords; 1.1509 +} 1.1510 + 1.1511 +static const UEnumeration gKeywordsEnum = { 1.1512 + NULL, 1.1513 + NULL, 1.1514 + uloc_kw_closeKeywords, 1.1515 + uloc_kw_countKeywords, 1.1516 + uenum_unextDefault, 1.1517 + uloc_kw_nextKeyword, 1.1518 + uloc_kw_resetKeywords 1.1519 +}; 1.1520 + 1.1521 +U_CAPI UEnumeration* U_EXPORT2 1.1522 +uloc_openKeywordList(const char *keywordList, int32_t keywordListSize, UErrorCode* status) 1.1523 +{ 1.1524 + UKeywordsContext *myContext = NULL; 1.1525 + UEnumeration *result = NULL; 1.1526 + 1.1527 + if(U_FAILURE(*status)) { 1.1528 + return NULL; 1.1529 + } 1.1530 + result = (UEnumeration *)uprv_malloc(sizeof(UEnumeration)); 1.1531 + /* Null pointer test */ 1.1532 + if (result == NULL) { 1.1533 + *status = U_MEMORY_ALLOCATION_ERROR; 1.1534 + return NULL; 1.1535 + } 1.1536 + uprv_memcpy(result, &gKeywordsEnum, sizeof(UEnumeration)); 1.1537 + myContext = static_cast<UKeywordsContext *>(uprv_malloc(sizeof(UKeywordsContext))); 1.1538 + if (myContext == NULL) { 1.1539 + *status = U_MEMORY_ALLOCATION_ERROR; 1.1540 + uprv_free(result); 1.1541 + return NULL; 1.1542 + } 1.1543 + myContext->keywords = (char *)uprv_malloc(keywordListSize+1); 1.1544 + uprv_memcpy(myContext->keywords, keywordList, keywordListSize); 1.1545 + myContext->keywords[keywordListSize] = 0; 1.1546 + myContext->current = myContext->keywords; 1.1547 + result->context = myContext; 1.1548 + return result; 1.1549 +} 1.1550 + 1.1551 +U_CAPI UEnumeration* U_EXPORT2 1.1552 +uloc_openKeywords(const char* localeID, 1.1553 + UErrorCode* status) 1.1554 +{ 1.1555 + int32_t i=0; 1.1556 + char keywords[256]; 1.1557 + int32_t keywordsCapacity = 256; 1.1558 + char tempBuffer[ULOC_FULLNAME_CAPACITY]; 1.1559 + const char* tmpLocaleID; 1.1560 + 1.1561 + if(status==NULL || U_FAILURE(*status)) { 1.1562 + return 0; 1.1563 + } 1.1564 + 1.1565 + if (_hasBCP47Extension(localeID)) { 1.1566 + _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), status); 1.1567 + } else { 1.1568 + if (localeID==NULL) { 1.1569 + localeID=uloc_getDefault(); 1.1570 + } 1.1571 + tmpLocaleID=localeID; 1.1572 + } 1.1573 + 1.1574 + /* Skip the language */ 1.1575 + ulocimp_getLanguage(tmpLocaleID, NULL, 0, &tmpLocaleID); 1.1576 + if(_isIDSeparator(*tmpLocaleID)) { 1.1577 + const char *scriptID; 1.1578 + /* Skip the script if available */ 1.1579 + ulocimp_getScript(tmpLocaleID+1, NULL, 0, &scriptID); 1.1580 + if(scriptID != tmpLocaleID+1) { 1.1581 + /* Found optional script */ 1.1582 + tmpLocaleID = scriptID; 1.1583 + } 1.1584 + /* Skip the Country */ 1.1585 + if (_isIDSeparator(*tmpLocaleID)) { 1.1586 + ulocimp_getCountry(tmpLocaleID+1, NULL, 0, &tmpLocaleID); 1.1587 + if(_isIDSeparator(*tmpLocaleID)) { 1.1588 + _getVariant(tmpLocaleID+1, *tmpLocaleID, NULL, 0); 1.1589 + } 1.1590 + } 1.1591 + } 1.1592 + 1.1593 + /* keywords are located after '@' */ 1.1594 + if((tmpLocaleID = locale_getKeywordsStart(tmpLocaleID)) != NULL) { 1.1595 + i=locale_getKeywords(tmpLocaleID+1, '@', keywords, keywordsCapacity, NULL, 0, NULL, FALSE, status); 1.1596 + } 1.1597 + 1.1598 + if(i) { 1.1599 + return uloc_openKeywordList(keywords, i, status); 1.1600 + } else { 1.1601 + return NULL; 1.1602 + } 1.1603 +} 1.1604 + 1.1605 + 1.1606 +/* bit-flags for 'options' parameter of _canonicalize */ 1.1607 +#define _ULOC_STRIP_KEYWORDS 0x2 1.1608 +#define _ULOC_CANONICALIZE 0x1 1.1609 + 1.1610 +#define OPTION_SET(options, mask) ((options & mask) != 0) 1.1611 + 1.1612 +static const char i_default[] = {'i', '-', 'd', 'e', 'f', 'a', 'u', 'l', 't'}; 1.1613 +#define I_DEFAULT_LENGTH (sizeof i_default / sizeof i_default[0]) 1.1614 + 1.1615 +/** 1.1616 + * Canonicalize the given localeID, to level 1 or to level 2, 1.1617 + * depending on the options. To specify level 1, pass in options=0. 1.1618 + * To specify level 2, pass in options=_ULOC_CANONICALIZE. 1.1619 + * 1.1620 + * This is the code underlying uloc_getName and uloc_canonicalize. 1.1621 + */ 1.1622 +static int32_t 1.1623 +_canonicalize(const char* localeID, 1.1624 + char* result, 1.1625 + int32_t resultCapacity, 1.1626 + uint32_t options, 1.1627 + UErrorCode* err) { 1.1628 + int32_t j, len, fieldCount=0, scriptSize=0, variantSize=0, nameCapacity; 1.1629 + char localeBuffer[ULOC_FULLNAME_CAPACITY]; 1.1630 + char tempBuffer[ULOC_FULLNAME_CAPACITY]; 1.1631 + const char* origLocaleID; 1.1632 + const char* tmpLocaleID; 1.1633 + const char* keywordAssign = NULL; 1.1634 + const char* separatorIndicator = NULL; 1.1635 + const char* addKeyword = NULL; 1.1636 + const char* addValue = NULL; 1.1637 + char* name; 1.1638 + char* variant = NULL; /* pointer into name, or NULL */ 1.1639 + 1.1640 + if (U_FAILURE(*err)) { 1.1641 + return 0; 1.1642 + } 1.1643 + 1.1644 + if (_hasBCP47Extension(localeID)) { 1.1645 + _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), err); 1.1646 + } else { 1.1647 + if (localeID==NULL) { 1.1648 + localeID=uloc_getDefault(); 1.1649 + } 1.1650 + tmpLocaleID=localeID; 1.1651 + } 1.1652 + 1.1653 + origLocaleID=tmpLocaleID; 1.1654 + 1.1655 + /* if we are doing a full canonicalization, then put results in 1.1656 + localeBuffer, if necessary; otherwise send them to result. */ 1.1657 + if (/*OPTION_SET(options, _ULOC_CANONICALIZE) &&*/ 1.1658 + (result == NULL || resultCapacity < (int32_t)sizeof(localeBuffer))) { 1.1659 + name = localeBuffer; 1.1660 + nameCapacity = (int32_t)sizeof(localeBuffer); 1.1661 + } else { 1.1662 + name = result; 1.1663 + nameCapacity = resultCapacity; 1.1664 + } 1.1665 + 1.1666 + /* get all pieces, one after another, and separate with '_' */ 1.1667 + len=ulocimp_getLanguage(tmpLocaleID, name, nameCapacity, &tmpLocaleID); 1.1668 + 1.1669 + if(len == I_DEFAULT_LENGTH && uprv_strncmp(origLocaleID, i_default, len) == 0) { 1.1670 + const char *d = uloc_getDefault(); 1.1671 + 1.1672 + len = (int32_t)uprv_strlen(d); 1.1673 + 1.1674 + if (name != NULL) { 1.1675 + uprv_strncpy(name, d, len); 1.1676 + } 1.1677 + } else if(_isIDSeparator(*tmpLocaleID)) { 1.1678 + const char *scriptID; 1.1679 + 1.1680 + ++fieldCount; 1.1681 + if(len<nameCapacity) { 1.1682 + name[len]='_'; 1.1683 + } 1.1684 + ++len; 1.1685 + 1.1686 + scriptSize=ulocimp_getScript(tmpLocaleID+1, 1.1687 + (len<nameCapacity ? name+len : NULL), nameCapacity-len, &scriptID); 1.1688 + if(scriptSize > 0) { 1.1689 + /* Found optional script */ 1.1690 + tmpLocaleID = scriptID; 1.1691 + ++fieldCount; 1.1692 + len+=scriptSize; 1.1693 + if (_isIDSeparator(*tmpLocaleID)) { 1.1694 + /* If there is something else, then we add the _ */ 1.1695 + if(len<nameCapacity) { 1.1696 + name[len]='_'; 1.1697 + } 1.1698 + ++len; 1.1699 + } 1.1700 + } 1.1701 + 1.1702 + if (_isIDSeparator(*tmpLocaleID)) { 1.1703 + const char *cntryID; 1.1704 + int32_t cntrySize = ulocimp_getCountry(tmpLocaleID+1, 1.1705 + (len<nameCapacity ? name+len : NULL), nameCapacity-len, &cntryID); 1.1706 + if (cntrySize > 0) { 1.1707 + /* Found optional country */ 1.1708 + tmpLocaleID = cntryID; 1.1709 + len+=cntrySize; 1.1710 + } 1.1711 + if(_isIDSeparator(*tmpLocaleID)) { 1.1712 + /* If there is something else, then we add the _ if we found country before. */ 1.1713 + if (cntrySize >= 0 && ! _isIDSeparator(*(tmpLocaleID+1)) ) { 1.1714 + ++fieldCount; 1.1715 + if(len<nameCapacity) { 1.1716 + name[len]='_'; 1.1717 + } 1.1718 + ++len; 1.1719 + } 1.1720 + 1.1721 + variantSize = _getVariant(tmpLocaleID+1, *tmpLocaleID, 1.1722 + (len<nameCapacity ? name+len : NULL), nameCapacity-len); 1.1723 + if (variantSize > 0) { 1.1724 + variant = len<nameCapacity ? name+len : NULL; 1.1725 + len += variantSize; 1.1726 + tmpLocaleID += variantSize + 1; /* skip '_' and variant */ 1.1727 + } 1.1728 + } 1.1729 + } 1.1730 + } 1.1731 + 1.1732 + /* Copy POSIX-style charset specifier, if any [mr.utf8] */ 1.1733 + if (!OPTION_SET(options, _ULOC_CANONICALIZE) && *tmpLocaleID == '.') { 1.1734 + UBool done = FALSE; 1.1735 + do { 1.1736 + char c = *tmpLocaleID; 1.1737 + switch (c) { 1.1738 + case 0: 1.1739 + case '@': 1.1740 + done = TRUE; 1.1741 + break; 1.1742 + default: 1.1743 + if (len<nameCapacity) { 1.1744 + name[len] = c; 1.1745 + } 1.1746 + ++len; 1.1747 + ++tmpLocaleID; 1.1748 + break; 1.1749 + } 1.1750 + } while (!done); 1.1751 + } 1.1752 + 1.1753 + /* Scan ahead to next '@' and determine if it is followed by '=' and/or ';' 1.1754 + After this, tmpLocaleID either points to '@' or is NULL */ 1.1755 + if ((tmpLocaleID=locale_getKeywordsStart(tmpLocaleID))!=NULL) { 1.1756 + keywordAssign = uprv_strchr(tmpLocaleID, '='); 1.1757 + separatorIndicator = uprv_strchr(tmpLocaleID, ';'); 1.1758 + } 1.1759 + 1.1760 + /* Copy POSIX-style variant, if any [mr@FOO] */ 1.1761 + if (!OPTION_SET(options, _ULOC_CANONICALIZE) && 1.1762 + tmpLocaleID != NULL && keywordAssign == NULL) { 1.1763 + for (;;) { 1.1764 + char c = *tmpLocaleID; 1.1765 + if (c == 0) { 1.1766 + break; 1.1767 + } 1.1768 + if (len<nameCapacity) { 1.1769 + name[len] = c; 1.1770 + } 1.1771 + ++len; 1.1772 + ++tmpLocaleID; 1.1773 + } 1.1774 + } 1.1775 + 1.1776 + if (OPTION_SET(options, _ULOC_CANONICALIZE)) { 1.1777 + /* Handle @FOO variant if @ is present and not followed by = */ 1.1778 + if (tmpLocaleID!=NULL && keywordAssign==NULL) { 1.1779 + int32_t posixVariantSize; 1.1780 + /* Add missing '_' if needed */ 1.1781 + if (fieldCount < 2 || (fieldCount < 3 && scriptSize > 0)) { 1.1782 + do { 1.1783 + if(len<nameCapacity) { 1.1784 + name[len]='_'; 1.1785 + } 1.1786 + ++len; 1.1787 + ++fieldCount; 1.1788 + } while(fieldCount<2); 1.1789 + } 1.1790 + posixVariantSize = _getVariantEx(tmpLocaleID+1, '@', name+len, nameCapacity-len, 1.1791 + (UBool)(variantSize > 0)); 1.1792 + if (posixVariantSize > 0) { 1.1793 + if (variant == NULL) { 1.1794 + variant = name+len; 1.1795 + } 1.1796 + len += posixVariantSize; 1.1797 + variantSize += posixVariantSize; 1.1798 + } 1.1799 + } 1.1800 + 1.1801 + /* Handle generic variants first */ 1.1802 + if (variant) { 1.1803 + for (j=0; j<(int32_t)(sizeof(VARIANT_MAP)/sizeof(VARIANT_MAP[0])); j++) { 1.1804 + const char* variantToCompare = VARIANT_MAP[j].variant; 1.1805 + int32_t n = (int32_t)uprv_strlen(variantToCompare); 1.1806 + int32_t variantLen = _deleteVariant(variant, uprv_min(variantSize, (nameCapacity-len)), variantToCompare, n); 1.1807 + len -= variantLen; 1.1808 + if (variantLen > 0) { 1.1809 + if (len > 0 && name[len-1] == '_') { /* delete trailing '_' */ 1.1810 + --len; 1.1811 + } 1.1812 + addKeyword = VARIANT_MAP[j].keyword; 1.1813 + addValue = VARIANT_MAP[j].value; 1.1814 + break; 1.1815 + } 1.1816 + } 1.1817 + if (len > 0 && len <= nameCapacity && name[len-1] == '_') { /* delete trailing '_' */ 1.1818 + --len; 1.1819 + } 1.1820 + } 1.1821 + 1.1822 + /* Look up the ID in the canonicalization map */ 1.1823 + for (j=0; j<(int32_t)(sizeof(CANONICALIZE_MAP)/sizeof(CANONICALIZE_MAP[0])); j++) { 1.1824 + const char* id = CANONICALIZE_MAP[j].id; 1.1825 + int32_t n = (int32_t)uprv_strlen(id); 1.1826 + if (len == n && uprv_strncmp(name, id, n) == 0) { 1.1827 + if (n == 0 && tmpLocaleID != NULL) { 1.1828 + break; /* Don't remap "" if keywords present */ 1.1829 + } 1.1830 + len = _copyCount(name, nameCapacity, CANONICALIZE_MAP[j].canonicalID); 1.1831 + if (CANONICALIZE_MAP[j].keyword) { 1.1832 + addKeyword = CANONICALIZE_MAP[j].keyword; 1.1833 + addValue = CANONICALIZE_MAP[j].value; 1.1834 + } 1.1835 + break; 1.1836 + } 1.1837 + } 1.1838 + } 1.1839 + 1.1840 + if (!OPTION_SET(options, _ULOC_STRIP_KEYWORDS)) { 1.1841 + if (tmpLocaleID!=NULL && keywordAssign!=NULL && 1.1842 + (!separatorIndicator || separatorIndicator > keywordAssign)) { 1.1843 + if(len<nameCapacity) { 1.1844 + name[len]='@'; 1.1845 + } 1.1846 + ++len; 1.1847 + ++fieldCount; 1.1848 + len += _getKeywords(tmpLocaleID+1, '@', (len<nameCapacity ? name+len : NULL), nameCapacity-len, 1.1849 + NULL, 0, NULL, TRUE, addKeyword, addValue, err); 1.1850 + } else if (addKeyword != NULL) { 1.1851 + U_ASSERT(addValue != NULL && len < nameCapacity); 1.1852 + /* inelegant but works -- later make _getKeywords do this? */ 1.1853 + len += _copyCount(name+len, nameCapacity-len, "@"); 1.1854 + len += _copyCount(name+len, nameCapacity-len, addKeyword); 1.1855 + len += _copyCount(name+len, nameCapacity-len, "="); 1.1856 + len += _copyCount(name+len, nameCapacity-len, addValue); 1.1857 + } 1.1858 + } 1.1859 + 1.1860 + if (U_SUCCESS(*err) && result != NULL && name == localeBuffer) { 1.1861 + uprv_strncpy(result, localeBuffer, (len > resultCapacity) ? resultCapacity : len); 1.1862 + } 1.1863 + 1.1864 + return u_terminateChars(result, resultCapacity, len, err); 1.1865 +} 1.1866 + 1.1867 +/* ### ID parsing API **************************************************/ 1.1868 + 1.1869 +U_CAPI int32_t U_EXPORT2 1.1870 +uloc_getParent(const char* localeID, 1.1871 + char* parent, 1.1872 + int32_t parentCapacity, 1.1873 + UErrorCode* err) 1.1874 +{ 1.1875 + const char *lastUnderscore; 1.1876 + int32_t i; 1.1877 + 1.1878 + if (U_FAILURE(*err)) 1.1879 + return 0; 1.1880 + 1.1881 + if (localeID == NULL) 1.1882 + localeID = uloc_getDefault(); 1.1883 + 1.1884 + lastUnderscore=uprv_strrchr(localeID, '_'); 1.1885 + if(lastUnderscore!=NULL) { 1.1886 + i=(int32_t)(lastUnderscore-localeID); 1.1887 + } else { 1.1888 + i=0; 1.1889 + } 1.1890 + 1.1891 + if(i>0 && parent != localeID) { 1.1892 + uprv_memcpy(parent, localeID, uprv_min(i, parentCapacity)); 1.1893 + } 1.1894 + return u_terminateChars(parent, parentCapacity, i, err); 1.1895 +} 1.1896 + 1.1897 +U_CAPI int32_t U_EXPORT2 1.1898 +uloc_getLanguage(const char* localeID, 1.1899 + char* language, 1.1900 + int32_t languageCapacity, 1.1901 + UErrorCode* err) 1.1902 +{ 1.1903 + /* uloc_getLanguage will return a 2 character iso-639 code if one exists. *CWB*/ 1.1904 + int32_t i=0; 1.1905 + 1.1906 + if (err==NULL || U_FAILURE(*err)) { 1.1907 + return 0; 1.1908 + } 1.1909 + 1.1910 + if(localeID==NULL) { 1.1911 + localeID=uloc_getDefault(); 1.1912 + } 1.1913 + 1.1914 + i=ulocimp_getLanguage(localeID, language, languageCapacity, NULL); 1.1915 + return u_terminateChars(language, languageCapacity, i, err); 1.1916 +} 1.1917 + 1.1918 +U_CAPI int32_t U_EXPORT2 1.1919 +uloc_getScript(const char* localeID, 1.1920 + char* script, 1.1921 + int32_t scriptCapacity, 1.1922 + UErrorCode* err) 1.1923 +{ 1.1924 + int32_t i=0; 1.1925 + 1.1926 + if(err==NULL || U_FAILURE(*err)) { 1.1927 + return 0; 1.1928 + } 1.1929 + 1.1930 + if(localeID==NULL) { 1.1931 + localeID=uloc_getDefault(); 1.1932 + } 1.1933 + 1.1934 + /* skip the language */ 1.1935 + ulocimp_getLanguage(localeID, NULL, 0, &localeID); 1.1936 + if(_isIDSeparator(*localeID)) { 1.1937 + i=ulocimp_getScript(localeID+1, script, scriptCapacity, NULL); 1.1938 + } 1.1939 + return u_terminateChars(script, scriptCapacity, i, err); 1.1940 +} 1.1941 + 1.1942 +U_CAPI int32_t U_EXPORT2 1.1943 +uloc_getCountry(const char* localeID, 1.1944 + char* country, 1.1945 + int32_t countryCapacity, 1.1946 + UErrorCode* err) 1.1947 +{ 1.1948 + int32_t i=0; 1.1949 + 1.1950 + if(err==NULL || U_FAILURE(*err)) { 1.1951 + return 0; 1.1952 + } 1.1953 + 1.1954 + if(localeID==NULL) { 1.1955 + localeID=uloc_getDefault(); 1.1956 + } 1.1957 + 1.1958 + /* Skip the language */ 1.1959 + ulocimp_getLanguage(localeID, NULL, 0, &localeID); 1.1960 + if(_isIDSeparator(*localeID)) { 1.1961 + const char *scriptID; 1.1962 + /* Skip the script if available */ 1.1963 + ulocimp_getScript(localeID+1, NULL, 0, &scriptID); 1.1964 + if(scriptID != localeID+1) { 1.1965 + /* Found optional script */ 1.1966 + localeID = scriptID; 1.1967 + } 1.1968 + if(_isIDSeparator(*localeID)) { 1.1969 + i=ulocimp_getCountry(localeID+1, country, countryCapacity, NULL); 1.1970 + } 1.1971 + } 1.1972 + return u_terminateChars(country, countryCapacity, i, err); 1.1973 +} 1.1974 + 1.1975 +U_CAPI int32_t U_EXPORT2 1.1976 +uloc_getVariant(const char* localeID, 1.1977 + char* variant, 1.1978 + int32_t variantCapacity, 1.1979 + UErrorCode* err) 1.1980 +{ 1.1981 + char tempBuffer[ULOC_FULLNAME_CAPACITY]; 1.1982 + const char* tmpLocaleID; 1.1983 + int32_t i=0; 1.1984 + 1.1985 + if(err==NULL || U_FAILURE(*err)) { 1.1986 + return 0; 1.1987 + } 1.1988 + 1.1989 + if (_hasBCP47Extension(localeID)) { 1.1990 + _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), err); 1.1991 + } else { 1.1992 + if (localeID==NULL) { 1.1993 + localeID=uloc_getDefault(); 1.1994 + } 1.1995 + tmpLocaleID=localeID; 1.1996 + } 1.1997 + 1.1998 + /* Skip the language */ 1.1999 + ulocimp_getLanguage(tmpLocaleID, NULL, 0, &tmpLocaleID); 1.2000 + if(_isIDSeparator(*tmpLocaleID)) { 1.2001 + const char *scriptID; 1.2002 + /* Skip the script if available */ 1.2003 + ulocimp_getScript(tmpLocaleID+1, NULL, 0, &scriptID); 1.2004 + if(scriptID != tmpLocaleID+1) { 1.2005 + /* Found optional script */ 1.2006 + tmpLocaleID = scriptID; 1.2007 + } 1.2008 + /* Skip the Country */ 1.2009 + if (_isIDSeparator(*tmpLocaleID)) { 1.2010 + const char *cntryID; 1.2011 + ulocimp_getCountry(tmpLocaleID+1, NULL, 0, &cntryID); 1.2012 + if (cntryID != tmpLocaleID+1) { 1.2013 + /* Found optional country */ 1.2014 + tmpLocaleID = cntryID; 1.2015 + } 1.2016 + if(_isIDSeparator(*tmpLocaleID)) { 1.2017 + /* If there was no country ID, skip a possible extra IDSeparator */ 1.2018 + if (tmpLocaleID != cntryID && _isIDSeparator(tmpLocaleID[1])) { 1.2019 + tmpLocaleID++; 1.2020 + } 1.2021 + i=_getVariant(tmpLocaleID+1, *tmpLocaleID, variant, variantCapacity); 1.2022 + } 1.2023 + } 1.2024 + } 1.2025 + 1.2026 + /* removed by weiv. We don't want to handle POSIX variants anymore. Use canonicalization function */ 1.2027 + /* if we do not have a variant tag yet then try a POSIX variant after '@' */ 1.2028 +/* 1.2029 + if(!haveVariant && (localeID=uprv_strrchr(localeID, '@'))!=NULL) { 1.2030 + i=_getVariant(localeID+1, '@', variant, variantCapacity); 1.2031 + } 1.2032 +*/ 1.2033 + return u_terminateChars(variant, variantCapacity, i, err); 1.2034 +} 1.2035 + 1.2036 +U_CAPI int32_t U_EXPORT2 1.2037 +uloc_getName(const char* localeID, 1.2038 + char* name, 1.2039 + int32_t nameCapacity, 1.2040 + UErrorCode* err) 1.2041 +{ 1.2042 + return _canonicalize(localeID, name, nameCapacity, 0, err); 1.2043 +} 1.2044 + 1.2045 +U_CAPI int32_t U_EXPORT2 1.2046 +uloc_getBaseName(const char* localeID, 1.2047 + char* name, 1.2048 + int32_t nameCapacity, 1.2049 + UErrorCode* err) 1.2050 +{ 1.2051 + return _canonicalize(localeID, name, nameCapacity, _ULOC_STRIP_KEYWORDS, err); 1.2052 +} 1.2053 + 1.2054 +U_CAPI int32_t U_EXPORT2 1.2055 +uloc_canonicalize(const char* localeID, 1.2056 + char* name, 1.2057 + int32_t nameCapacity, 1.2058 + UErrorCode* err) 1.2059 +{ 1.2060 + return _canonicalize(localeID, name, nameCapacity, _ULOC_CANONICALIZE, err); 1.2061 +} 1.2062 + 1.2063 +U_CAPI const char* U_EXPORT2 1.2064 +uloc_getISO3Language(const char* localeID) 1.2065 +{ 1.2066 + int16_t offset; 1.2067 + char lang[ULOC_LANG_CAPACITY]; 1.2068 + UErrorCode err = U_ZERO_ERROR; 1.2069 + 1.2070 + if (localeID == NULL) 1.2071 + { 1.2072 + localeID = uloc_getDefault(); 1.2073 + } 1.2074 + uloc_getLanguage(localeID, lang, ULOC_LANG_CAPACITY, &err); 1.2075 + if (U_FAILURE(err)) 1.2076 + return ""; 1.2077 + offset = _findIndex(LANGUAGES, lang); 1.2078 + if (offset < 0) 1.2079 + return ""; 1.2080 + return LANGUAGES_3[offset]; 1.2081 +} 1.2082 + 1.2083 +U_CAPI const char* U_EXPORT2 1.2084 +uloc_getISO3Country(const char* localeID) 1.2085 +{ 1.2086 + int16_t offset; 1.2087 + char cntry[ULOC_LANG_CAPACITY]; 1.2088 + UErrorCode err = U_ZERO_ERROR; 1.2089 + 1.2090 + if (localeID == NULL) 1.2091 + { 1.2092 + localeID = uloc_getDefault(); 1.2093 + } 1.2094 + uloc_getCountry(localeID, cntry, ULOC_LANG_CAPACITY, &err); 1.2095 + if (U_FAILURE(err)) 1.2096 + return ""; 1.2097 + offset = _findIndex(COUNTRIES, cntry); 1.2098 + if (offset < 0) 1.2099 + return ""; 1.2100 + 1.2101 + return COUNTRIES_3[offset]; 1.2102 +} 1.2103 + 1.2104 +U_CAPI uint32_t U_EXPORT2 1.2105 +uloc_getLCID(const char* localeID) 1.2106 +{ 1.2107 + UErrorCode status = U_ZERO_ERROR; 1.2108 + char langID[ULOC_FULLNAME_CAPACITY]; 1.2109 + 1.2110 + uloc_getLanguage(localeID, langID, sizeof(langID), &status); 1.2111 + if (U_FAILURE(status)) { 1.2112 + return 0; 1.2113 + } 1.2114 + 1.2115 + if (uprv_strchr(localeID, '@')) { 1.2116 + // uprv_convertToLCID does not support keywords other than collation. 1.2117 + // Remove all keywords except collation. 1.2118 + int32_t len; 1.2119 + char collVal[ULOC_KEYWORDS_CAPACITY]; 1.2120 + char tmpLocaleID[ULOC_FULLNAME_CAPACITY]; 1.2121 + 1.2122 + len = uloc_getKeywordValue(localeID, "collation", collVal, 1.2123 + sizeof(collVal)/sizeof(collVal[0]) - 1, &status); 1.2124 + 1.2125 + if (U_SUCCESS(status) && len > 0) { 1.2126 + collVal[len] = 0; 1.2127 + 1.2128 + len = uloc_getBaseName(localeID, tmpLocaleID, 1.2129 + sizeof(tmpLocaleID)/sizeof(tmpLocaleID[0]) - 1, &status); 1.2130 + 1.2131 + if (U_SUCCESS(status)) { 1.2132 + tmpLocaleID[len] = 0; 1.2133 + 1.2134 + len = uloc_setKeywordValue("collation", collVal, tmpLocaleID, 1.2135 + sizeof(tmpLocaleID)/sizeof(tmpLocaleID[0]) - len - 1, &status); 1.2136 + 1.2137 + if (U_SUCCESS(status)) { 1.2138 + tmpLocaleID[len] = 0; 1.2139 + return uprv_convertToLCID(langID, tmpLocaleID, &status); 1.2140 + } 1.2141 + } 1.2142 + } 1.2143 + 1.2144 + // fall through - all keywords are simply ignored 1.2145 + status = U_ZERO_ERROR; 1.2146 + } 1.2147 + 1.2148 + return uprv_convertToLCID(langID, localeID, &status); 1.2149 +} 1.2150 + 1.2151 +U_CAPI int32_t U_EXPORT2 1.2152 +uloc_getLocaleForLCID(uint32_t hostid, char *locale, int32_t localeCapacity, 1.2153 + UErrorCode *status) 1.2154 +{ 1.2155 + return uprv_convertToPosix(hostid, locale, localeCapacity, status); 1.2156 +} 1.2157 + 1.2158 +/* ### Default locale **************************************************/ 1.2159 + 1.2160 +U_CAPI const char* U_EXPORT2 1.2161 +uloc_getDefault() 1.2162 +{ 1.2163 + return locale_get_default(); 1.2164 +} 1.2165 + 1.2166 +U_CAPI void U_EXPORT2 1.2167 +uloc_setDefault(const char* newDefaultLocale, 1.2168 + UErrorCode* err) 1.2169 +{ 1.2170 + if (U_FAILURE(*err)) 1.2171 + return; 1.2172 + /* the error code isn't currently used for anything by this function*/ 1.2173 + 1.2174 + /* propagate change to C++ */ 1.2175 + locale_set_default(newDefaultLocale); 1.2176 +} 1.2177 + 1.2178 +/** 1.2179 + * Returns a list of all 2-letter language codes defined in ISO 639. This is a pointer 1.2180 + * to an array of pointers to arrays of char. All of these pointers are owned 1.2181 + * by ICU-- do not delete them, and do not write through them. The array is 1.2182 + * terminated with a null pointer. 1.2183 + */ 1.2184 +U_CAPI const char* const* U_EXPORT2 1.2185 +uloc_getISOLanguages() 1.2186 +{ 1.2187 + return LANGUAGES; 1.2188 +} 1.2189 + 1.2190 +/** 1.2191 + * Returns a list of all 2-letter country codes defined in ISO 639. This is a 1.2192 + * pointer to an array of pointers to arrays of char. All of these pointers are 1.2193 + * owned by ICU-- do not delete them, and do not write through them. The array is 1.2194 + * terminated with a null pointer. 1.2195 + */ 1.2196 +U_CAPI const char* const* U_EXPORT2 1.2197 +uloc_getISOCountries() 1.2198 +{ 1.2199 + return COUNTRIES; 1.2200 +} 1.2201 + 1.2202 + 1.2203 +/* this function to be moved into cstring.c later */ 1.2204 +static char gDecimal = 0; 1.2205 + 1.2206 +static /* U_CAPI */ 1.2207 +double 1.2208 +/* U_EXPORT2 */ 1.2209 +_uloc_strtod(const char *start, char **end) { 1.2210 + char *decimal; 1.2211 + char *myEnd; 1.2212 + char buf[30]; 1.2213 + double rv; 1.2214 + if (!gDecimal) { 1.2215 + char rep[5]; 1.2216 + /* For machines that decide to change the decimal on you, 1.2217 + and try to be too smart with localization. 1.2218 + This normally should be just a '.'. */ 1.2219 + sprintf(rep, "%+1.1f", 1.0); 1.2220 + gDecimal = rep[2]; 1.2221 + } 1.2222 + 1.2223 + if(gDecimal == '.') { 1.2224 + return uprv_strtod(start, end); /* fall through to OS */ 1.2225 + } else { 1.2226 + uprv_strncpy(buf, start, 29); 1.2227 + buf[29]=0; 1.2228 + decimal = uprv_strchr(buf, '.'); 1.2229 + if(decimal) { 1.2230 + *decimal = gDecimal; 1.2231 + } else { 1.2232 + return uprv_strtod(start, end); /* no decimal point */ 1.2233 + } 1.2234 + rv = uprv_strtod(buf, &myEnd); 1.2235 + if(end) { 1.2236 + *end = (char*)(start+(myEnd-buf)); /* cast away const (to follow uprv_strtod API.) */ 1.2237 + } 1.2238 + return rv; 1.2239 + } 1.2240 +} 1.2241 + 1.2242 +typedef struct { 1.2243 + float q; 1.2244 + int32_t dummy; /* to avoid uninitialized memory copy from qsort */ 1.2245 + char *locale; 1.2246 +} _acceptLangItem; 1.2247 + 1.2248 +static int32_t U_CALLCONV 1.2249 +uloc_acceptLanguageCompare(const void * /*context*/, const void *a, const void *b) 1.2250 +{ 1.2251 + const _acceptLangItem *aa = (const _acceptLangItem*)a; 1.2252 + const _acceptLangItem *bb = (const _acceptLangItem*)b; 1.2253 + 1.2254 + int32_t rc = 0; 1.2255 + if(bb->q < aa->q) { 1.2256 + rc = -1; /* A > B */ 1.2257 + } else if(bb->q > aa->q) { 1.2258 + rc = 1; /* A < B */ 1.2259 + } else { 1.2260 + rc = 0; /* A = B */ 1.2261 + } 1.2262 + 1.2263 + if(rc==0) { 1.2264 + rc = uprv_stricmp(aa->locale, bb->locale); 1.2265 + } 1.2266 + 1.2267 +#if defined(ULOC_DEBUG) 1.2268 + /* fprintf(stderr, "a:[%s:%g], b:[%s:%g] -> %d\n", 1.2269 + aa->locale, aa->q, 1.2270 + bb->locale, bb->q, 1.2271 + rc);*/ 1.2272 +#endif 1.2273 + 1.2274 + return rc; 1.2275 +} 1.2276 + 1.2277 +/* 1.2278 +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 1.2279 +*/ 1.2280 + 1.2281 +U_CAPI int32_t U_EXPORT2 1.2282 +uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable, UAcceptResult *outResult, 1.2283 + const char *httpAcceptLanguage, 1.2284 + UEnumeration* availableLocales, 1.2285 + UErrorCode *status) 1.2286 +{ 1.2287 + _acceptLangItem *j; 1.2288 + _acceptLangItem smallBuffer[30]; 1.2289 + char **strs; 1.2290 + char tmp[ULOC_FULLNAME_CAPACITY +1]; 1.2291 + int32_t n = 0; 1.2292 + const char *itemEnd; 1.2293 + const char *paramEnd; 1.2294 + const char *s; 1.2295 + const char *t; 1.2296 + int32_t res; 1.2297 + int32_t i; 1.2298 + int32_t l = (int32_t)uprv_strlen(httpAcceptLanguage); 1.2299 + int32_t jSize; 1.2300 + char *tempstr; /* Use for null pointer check */ 1.2301 + 1.2302 + j = smallBuffer; 1.2303 + jSize = sizeof(smallBuffer)/sizeof(smallBuffer[0]); 1.2304 + if(U_FAILURE(*status)) { 1.2305 + return -1; 1.2306 + } 1.2307 + 1.2308 + for(s=httpAcceptLanguage;s&&*s;) { 1.2309 + while(isspace(*s)) /* eat space at the beginning */ 1.2310 + s++; 1.2311 + itemEnd=uprv_strchr(s,','); 1.2312 + paramEnd=uprv_strchr(s,';'); 1.2313 + if(!itemEnd) { 1.2314 + itemEnd = httpAcceptLanguage+l; /* end of string */ 1.2315 + } 1.2316 + if(paramEnd && paramEnd<itemEnd) { 1.2317 + /* semicolon (;) is closer than end (,) */ 1.2318 + t = paramEnd+1; 1.2319 + if(*t=='q') { 1.2320 + t++; 1.2321 + } 1.2322 + while(isspace(*t)) { 1.2323 + t++; 1.2324 + } 1.2325 + if(*t=='=') { 1.2326 + t++; 1.2327 + } 1.2328 + while(isspace(*t)) { 1.2329 + t++; 1.2330 + } 1.2331 + j[n].q = (float)_uloc_strtod(t,NULL); 1.2332 + } else { 1.2333 + /* no semicolon - it's 1.0 */ 1.2334 + j[n].q = 1.0f; 1.2335 + paramEnd = itemEnd; 1.2336 + } 1.2337 + j[n].dummy=0; 1.2338 + /* eat spaces prior to semi */ 1.2339 + for(t=(paramEnd-1);(paramEnd>s)&&isspace(*t);t--) 1.2340 + ; 1.2341 + /* Check for null pointer from uprv_strndup */ 1.2342 + tempstr = uprv_strndup(s,(int32_t)((t+1)-s)); 1.2343 + if (tempstr == NULL) { 1.2344 + *status = U_MEMORY_ALLOCATION_ERROR; 1.2345 + return -1; 1.2346 + } 1.2347 + j[n].locale = tempstr; 1.2348 + uloc_canonicalize(j[n].locale,tmp,sizeof(tmp)/sizeof(tmp[0]),status); 1.2349 + if(strcmp(j[n].locale,tmp)) { 1.2350 + uprv_free(j[n].locale); 1.2351 + j[n].locale=uprv_strdup(tmp); 1.2352 + } 1.2353 +#if defined(ULOC_DEBUG) 1.2354 + /*fprintf(stderr,"%d: s <%s> q <%g>\n", n, j[n].locale, j[n].q);*/ 1.2355 +#endif 1.2356 + n++; 1.2357 + s = itemEnd; 1.2358 + while(*s==',') { /* eat duplicate commas */ 1.2359 + s++; 1.2360 + } 1.2361 + if(n>=jSize) { 1.2362 + if(j==smallBuffer) { /* overflowed the small buffer. */ 1.2363 + j = static_cast<_acceptLangItem *>(uprv_malloc(sizeof(j[0])*(jSize*2))); 1.2364 + if(j!=NULL) { 1.2365 + uprv_memcpy(j,smallBuffer,sizeof(j[0])*jSize); 1.2366 + } 1.2367 +#if defined(ULOC_DEBUG) 1.2368 + fprintf(stderr,"malloced at size %d\n", jSize); 1.2369 +#endif 1.2370 + } else { 1.2371 + j = static_cast<_acceptLangItem *>(uprv_realloc(j, sizeof(j[0])*jSize*2)); 1.2372 +#if defined(ULOC_DEBUG) 1.2373 + fprintf(stderr,"re-alloced at size %d\n", jSize); 1.2374 +#endif 1.2375 + } 1.2376 + jSize *= 2; 1.2377 + if(j==NULL) { 1.2378 + *status = U_MEMORY_ALLOCATION_ERROR; 1.2379 + return -1; 1.2380 + } 1.2381 + } 1.2382 + } 1.2383 + uprv_sortArray(j, n, sizeof(j[0]), uloc_acceptLanguageCompare, NULL, TRUE, status); 1.2384 + if(U_FAILURE(*status)) { 1.2385 + if(j != smallBuffer) { 1.2386 +#if defined(ULOC_DEBUG) 1.2387 + fprintf(stderr,"freeing j %p\n", j); 1.2388 +#endif 1.2389 + uprv_free(j); 1.2390 + } 1.2391 + return -1; 1.2392 + } 1.2393 + strs = static_cast<char **>(uprv_malloc((size_t)(sizeof(strs[0])*n))); 1.2394 + /* Check for null pointer */ 1.2395 + if (strs == NULL) { 1.2396 + uprv_free(j); /* Free to avoid memory leak */ 1.2397 + *status = U_MEMORY_ALLOCATION_ERROR; 1.2398 + return -1; 1.2399 + } 1.2400 + for(i=0;i<n;i++) { 1.2401 +#if defined(ULOC_DEBUG) 1.2402 + /*fprintf(stderr,"%d: s <%s> q <%g>\n", i, j[i].locale, j[i].q);*/ 1.2403 +#endif 1.2404 + strs[i]=j[i].locale; 1.2405 + } 1.2406 + res = uloc_acceptLanguage(result, resultAvailable, outResult, 1.2407 + (const char**)strs, n, availableLocales, status); 1.2408 + for(i=0;i<n;i++) { 1.2409 + uprv_free(strs[i]); 1.2410 + } 1.2411 + uprv_free(strs); 1.2412 + if(j != smallBuffer) { 1.2413 +#if defined(ULOC_DEBUG) 1.2414 + fprintf(stderr,"freeing j %p\n", j); 1.2415 +#endif 1.2416 + uprv_free(j); 1.2417 + } 1.2418 + return res; 1.2419 +} 1.2420 + 1.2421 + 1.2422 +U_CAPI int32_t U_EXPORT2 1.2423 +uloc_acceptLanguage(char *result, int32_t resultAvailable, 1.2424 + UAcceptResult *outResult, const char **acceptList, 1.2425 + int32_t acceptListCount, 1.2426 + UEnumeration* availableLocales, 1.2427 + UErrorCode *status) 1.2428 +{ 1.2429 + int32_t i,j; 1.2430 + int32_t len; 1.2431 + int32_t maxLen=0; 1.2432 + char tmp[ULOC_FULLNAME_CAPACITY+1]; 1.2433 + const char *l; 1.2434 + char **fallbackList; 1.2435 + if(U_FAILURE(*status)) { 1.2436 + return -1; 1.2437 + } 1.2438 + fallbackList = static_cast<char **>(uprv_malloc((size_t)(sizeof(fallbackList[0])*acceptListCount))); 1.2439 + if(fallbackList==NULL) { 1.2440 + *status = U_MEMORY_ALLOCATION_ERROR; 1.2441 + return -1; 1.2442 + } 1.2443 + for(i=0;i<acceptListCount;i++) { 1.2444 +#if defined(ULOC_DEBUG) 1.2445 + fprintf(stderr,"%02d: %s\n", i, acceptList[i]); 1.2446 +#endif 1.2447 + while((l=uenum_next(availableLocales, NULL, status))) { 1.2448 +#if defined(ULOC_DEBUG) 1.2449 + fprintf(stderr," %s\n", l); 1.2450 +#endif 1.2451 + len = (int32_t)uprv_strlen(l); 1.2452 + if(!uprv_strcmp(acceptList[i], l)) { 1.2453 + if(outResult) { 1.2454 + *outResult = ULOC_ACCEPT_VALID; 1.2455 + } 1.2456 +#if defined(ULOC_DEBUG) 1.2457 + fprintf(stderr, "MATCH! %s\n", l); 1.2458 +#endif 1.2459 + if(len>0) { 1.2460 + uprv_strncpy(result, l, uprv_min(len, resultAvailable)); 1.2461 + } 1.2462 + for(j=0;j<i;j++) { 1.2463 + uprv_free(fallbackList[j]); 1.2464 + } 1.2465 + uprv_free(fallbackList); 1.2466 + return u_terminateChars(result, resultAvailable, len, status); 1.2467 + } 1.2468 + if(len>maxLen) { 1.2469 + maxLen = len; 1.2470 + } 1.2471 + } 1.2472 + uenum_reset(availableLocales, status); 1.2473 + /* save off parent info */ 1.2474 + if(uloc_getParent(acceptList[i], tmp, sizeof(tmp)/sizeof(tmp[0]), status)!=0) { 1.2475 + fallbackList[i] = uprv_strdup(tmp); 1.2476 + } else { 1.2477 + fallbackList[i]=0; 1.2478 + } 1.2479 + } 1.2480 + 1.2481 + for(maxLen--;maxLen>0;maxLen--) { 1.2482 + for(i=0;i<acceptListCount;i++) { 1.2483 + if(fallbackList[i] && ((int32_t)uprv_strlen(fallbackList[i])==maxLen)) { 1.2484 +#if defined(ULOC_DEBUG) 1.2485 + fprintf(stderr,"Try: [%s]", fallbackList[i]); 1.2486 +#endif 1.2487 + while((l=uenum_next(availableLocales, NULL, status))) { 1.2488 +#if defined(ULOC_DEBUG) 1.2489 + fprintf(stderr," %s\n", l); 1.2490 +#endif 1.2491 + len = (int32_t)uprv_strlen(l); 1.2492 + if(!uprv_strcmp(fallbackList[i], l)) { 1.2493 + if(outResult) { 1.2494 + *outResult = ULOC_ACCEPT_FALLBACK; 1.2495 + } 1.2496 +#if defined(ULOC_DEBUG) 1.2497 + fprintf(stderr, "fallback MATCH! %s\n", l); 1.2498 +#endif 1.2499 + if(len>0) { 1.2500 + uprv_strncpy(result, l, uprv_min(len, resultAvailable)); 1.2501 + } 1.2502 + for(j=0;j<acceptListCount;j++) { 1.2503 + uprv_free(fallbackList[j]); 1.2504 + } 1.2505 + uprv_free(fallbackList); 1.2506 + return u_terminateChars(result, resultAvailable, len, status); 1.2507 + } 1.2508 + } 1.2509 + uenum_reset(availableLocales, status); 1.2510 + 1.2511 + if(uloc_getParent(fallbackList[i], tmp, sizeof(tmp)/sizeof(tmp[0]), status)!=0) { 1.2512 + uprv_free(fallbackList[i]); 1.2513 + fallbackList[i] = uprv_strdup(tmp); 1.2514 + } else { 1.2515 + uprv_free(fallbackList[i]); 1.2516 + fallbackList[i]=0; 1.2517 + } 1.2518 + } 1.2519 + } 1.2520 + if(outResult) { 1.2521 + *outResult = ULOC_ACCEPT_FAILED; 1.2522 + } 1.2523 + } 1.2524 + for(i=0;i<acceptListCount;i++) { 1.2525 + uprv_free(fallbackList[i]); 1.2526 + } 1.2527 + uprv_free(fallbackList); 1.2528 + return -1; 1.2529 +} 1.2530 + 1.2531 +/*eof*/