+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
**********************************************************************
-* Copyright (C) 1997-2014, International Business Machines
+* Copyright (C) 1997-2016, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
*
#include "uarrsort.h"
#include "uenumimp.h"
#include "uassert.h"
+#include "charstr.h"
#include <stdio.h> /* for sprintf */
+U_NAMESPACE_USE
+
/* ### Declarations **************************************************/
/* Locale stuff from locid.cpp */
* The range qaa-qtz is reserved for local use
*/
/* Generated using org.unicode.cldr.icu.GenerateISO639LanguageTables */
-/* ISO639 table version is 20130531 */
+/* ISO639 table version is 20150505 */
+/* Subsequent hand addition of selected languages */
static const char * const LANGUAGES[] = {
- "aa", "ab", "ace", "ach", "ada", "ady", "ae", "af",
- "afa", "afh", "agq", "ain", "ak", "akk", "ale", "alg",
- "alt", "am", "an", "ang", "anp", "apa", "ar", "arc",
- "arn", "arp", "art", "arw", "as", "asa", "ast", "ath",
- "aus", "av", "awa", "ay", "az",
- "ba", "bad", "bai", "bal", "ban", "bas", "bat", "bax",
- "bbj", "be", "bej", "bem", "ber", "bez", "bfd", "bg",
- "bh", "bho", "bi", "bik", "bin", "bkm", "bla", "bm",
- "bn", "bnt", "bo", "br", "bra", "brx", "bs", "bss",
- "btk", "bua", "bug", "bum", "byn", "byv",
- "ca", "cad", "cai", "car", "cau", "cay", "cch", "ce",
- "ceb", "cel", "cgg", "ch", "chb", "chg", "chk", "chm",
- "chn", "cho", "chp", "chr", "chy", "ckb", "cmc", "co",
- "cop", "cpe", "cpf", "cpp", "cr", "crh", "crp", "cs",
- "csb", "cu", "cus", "cv", "cy",
- "da", "dak", "dar", "dav", "day", "de", "del", "den",
- "dgr", "din", "dje", "doi", "dra", "dsb", "dua", "dum",
- "dv", "dyo", "dyu", "dz", "dzg",
- "ebu", "ee", "efi", "egy", "eka", "el", "elx", "en",
- "enm", "eo", "es", "et", "eu", "ewo",
- "fa", "fan", "fat", "ff", "fi", "fil", "fiu", "fj",
- "fo", "fon", "fr", "frm", "fro", "frr", "frs", "fur",
- "fy",
- "ga", "gaa", "gay", "gba", "gd", "gem", "gez", "gil",
- "gl", "gmh", "gn", "goh", "gon", "gor", "got", "grb",
- "grc", "gsw", "gu", "guz", "gv", "gwi",
- "ha", "hai", "haw", "he", "hi", "hil", "him", "hit",
- "hmn", "ho", "hr", "hsb", "ht", "hu", "hup", "hy",
- "hz",
- "ia", "iba", "ibb", "id", "ie", "ig", "ii", "ijo",
- "ik", "ilo", "inc", "ine", "inh", "io", "ira", "iro",
- "is", "it", "iu",
- "ja", "jbo", "jgo", "jmc", "jpr", "jrb", "jv",
- "ka", "kaa", "kab", "kac", "kaj", "kam", "kar", "kaw",
- "kbd", "kbl", "kcg", "kde", "kea", "kfo", "kg", "kha",
- "khi", "kho", "khq", "ki", "kj", "kk", "kkj", "kl",
- "kln", "km", "kmb", "kn", "ko", "kok", "kos", "kpe",
- "kr", "krc", "krl", "kro", "kru", "ks", "ksb", "ksf",
- "ksh", "ku", "kum", "kut", "kv", "kw", "ky",
- "la", "lad", "lag", "lah", "lam", "lb", "lez", "lg",
- "li", "lkt", "ln", "lo", "lol", "loz", "lt", "lu",
- "lua", "lui", "lun", "luo", "lus", "luy", "lv",
- "mad", "maf", "mag", "mai", "mak", "man", "map", "mas",
- "mde", "mdf", "mdr", "men", "mer", "mfe", "mg", "mga",
- "mgh", "mgo", "mh", "mi", "mic", "min", "mis", "mk",
- "mkh", "ml", "mn", "mnc", "mni", "mno", "mo", "moh",
- "mos", "mr", "ms", "mt", "mua", "mul", "mun", "mus",
- "mwl", "mwr", "my", "mye", "myn", "myv",
- "na", "nah", "nai", "nap", "naq", "nb", "nd", "nds",
- "ne", "new", "ng", "nia", "nic", "niu", "nl", "nmg",
- "nn", "nnh", "no", "nog", "non", "nqo", "nr", "nso",
- "nub", "nus", "nv", "nwc", "ny", "nym", "nyn", "nyo",
- "nzi",
- "oc", "oj", "om", "or", "os", "osa", "ota", "oto",
- "pa", "paa", "pag", "pal", "pam", "pap", "pau", "peo",
- "phi", "phn", "pi", "pl", "pon", "pra", "pro", "ps",
- "pt",
- "qu",
- "raj", "rap", "rar", "rm", "rn", "ro", "roa", "rof",
- "rom", "ru", "rup", "rw", "rwk",
- "sa", "sad", "sah", "sai", "sal", "sam", "saq", "sas",
- "sat", "sba", "sbp", "sc", "scn", "sco", "sd", "se",
- "see", "seh", "sel", "sem", "ses", "sg", "sga", "sgn",
- "shi", "shn", "shu", "si", "sid", "sio", "sit",
- "sk", "sl", "sla", "sm", "sma", "smi", "smj", "smn",
- "sms", "sn", "snk", "so", "sog", "son", "sq", "sr",
- "srn", "srr", "ss", "ssa", "ssy", "st", "su", "suk",
- "sus", "sux", "sv", "sw", "swb", "swc", "syc", "syr",
- "ta", "tai", "te", "tem", "teo", "ter", "tet", "tg",
- "th", "ti", "tig", "tiv", "tk", "tkl", "tl", "tlh",
- "tli", "tmh", "tn", "to", "tog", "tpi", "tr", "trv",
- "ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw",
- "twq", "ty", "tyv", "tzm",
- "udm", "ug", "uga", "uk", "umb", "und", "ur", "uz",
- "vai", "ve", "vi", "vo", "vot", "vun",
- "wa", "wae", "wak", "wal", "war", "was", "wen", "wo",
- "xal", "xh", "xog",
- "yao", "yap", "yav", "ybb", "yi", "yo", "ypk", "yue",
- "za", "zap", "zbl", "zen", "zgh", "zh", "znd", "zu",
- "zun", "zxx", "zza",
+ "aa", "ab", "ace", "ach", "ada", "ady", "ae", "aeb",
+ "af", "afh", "agq", "ain", "ak", "akk", "akz", "ale",
+ "aln", "alt", "am", "an", "ang", "anp", "ar", "arc",
+ "arn", "aro", "arp", "arq", "ars", "arw", "ary", "arz", "as",
+ "asa", "ase", "ast", "av", "avk", "awa", "ay", "az",
+ "ba", "bal", "ban", "bar", "bas", "bax", "bbc", "bbj",
+ "be", "bej", "bem", "bew", "bez", "bfd", "bfq", "bg",
+ "bgn", "bho", "bi", "bik", "bin", "bjn", "bkm", "bla",
+ "bm", "bn", "bo", "bpy", "bqi", "br", "bra", "brh",
+ "brx", "bs", "bss", "bua", "bug", "bum", "byn", "byv",
+ "ca", "cad", "car", "cay", "cch", "ccp", "ce", "ceb", "cgg",
+ "ch", "chb", "chg", "chk", "chm", "chn", "cho", "chp",
+ "chr", "chy", "ckb", "co", "cop", "cps", "cr", "crh",
+ "cs", "csb", "cu", "cv", "cy",
+ "da", "dak", "dar", "dav", "de", "del", "den", "dgr",
+ "din", "dje", "doi", "dsb", "dtp", "dua", "dum", "dv",
+ "dyo", "dyu", "dz", "dzg",
+ "ebu", "ee", "efi", "egl", "egy", "eka", "el", "elx",
+ "en", "enm", "eo", "es", "esu", "et", "eu", "ewo",
+ "ext",
+ "fa", "fan", "fat", "ff", "fi", "fil", "fit", "fj",
+ "fo", "fon", "fr", "frc", "frm", "fro", "frp", "frr",
+ "frs", "fur", "fy",
+ "ga", "gaa", "gag", "gan", "gay", "gba", "gbz", "gd",
+ "gez", "gil", "gl", "glk", "gmh", "gn", "goh", "gom",
+ "gon", "gor", "got", "grb", "grc", "gsw", "gu", "guc",
+ "gur", "guz", "gv", "gwi",
+ "ha", "hai", "hak", "haw", "he", "hi", "hif", "hil",
+ "hit", "hmn", "ho", "hr", "hsb", "hsn", "ht", "hu",
+ "hup", "hy", "hz",
+ "ia", "iba", "ibb", "id", "ie", "ig", "ii", "ik",
+ "ilo", "inh", "io", "is", "it", "iu", "izh",
+ "ja", "jam", "jbo", "jgo", "jmc", "jpr", "jrb", "jut",
+ "jv",
+ "ka", "kaa", "kab", "kac", "kaj", "kam", "kaw", "kbd",
+ "kbl", "kcg", "kde", "kea", "ken", "kfo", "kg", "kgp",
+ "kha", "kho", "khq", "khw", "ki", "kiu", "kj", "kk",
+ "kkj", "kl", "kln", "km", "kmb", "kn", "ko", "koi",
+ "kok", "kos", "kpe", "kr", "krc", "kri", "krj", "krl",
+ "kru", "ks", "ksb", "ksf", "ksh", "ku", "kum", "kut",
+ "kv", "kw", "ky",
+ "la", "lad", "lag", "lah", "lam", "lb", "lez", "lfn",
+ "lg", "li", "lij", "liv", "lkt", "lmo", "ln", "lo",
+ "lol", "loz", "lrc", "lt", "ltg", "lu", "lua", "lui",
+ "lun", "luo", "lus", "luy", "lv", "lzh", "lzz",
+ "mad", "maf", "mag", "mai", "mak", "man", "mas", "mde",
+ "mdf", "mdh", "mdr", "men", "mer", "mfe", "mg", "mga",
+ "mgh", "mgo", "mh", "mi", "mic", "min", "mis", "mk",
+ "ml", "mn", "mnc", "mni", "mo",
+ "moh", "mos", "mr", "mrj",
+ "ms", "mt", "mua", "mul", "mus", "mwl", "mwr", "mwv",
+ "my", "mye", "myv", "mzn",
+ "na", "nan", "nap", "naq", "nb", "nd", "nds", "ne",
+ "new", "ng", "nia", "niu", "njo", "nl", "nmg", "nn",
+ "nnh", "no", "nog", "non", "nov", "nqo", "nr", "nso",
+ "nus", "nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi",
+ "oc", "oj", "om", "or", "os", "osa", "ota",
+ "pa", "pag", "pal", "pam", "pap", "pau", "pcd", "pdc",
+ "pdt", "peo", "pfl", "phn", "pi", "pl", "pms", "pnt",
+ "pon", "prg", "pro", "ps", "pt",
+ "qu", "quc", "qug",
+ "raj", "rap", "rar", "rgn", "rif", "rm", "rn", "ro",
+ "rof", "rom", "rtm", "ru", "rue", "rug", "rup",
+ "rw", "rwk",
+ "sa", "sad", "sah", "sam", "saq", "sas", "sat", "saz",
+ "sba", "sbp", "sc", "scn", "sco", "sd", "sdc", "sdh",
+ "se", "see", "seh", "sei", "sel", "ses", "sg", "sga",
+ "sgs", "shi", "shn", "shu", "si", "sid", "sk",
+ "sl", "sli", "sly", "sm", "sma", "smj", "smn", "sms",
+ "sn", "snk", "so", "sog", "sq", "sr", "srn", "srr",
+ "ss", "ssy", "st", "stq", "su", "suk", "sus", "sux",
+ "sv", "sw", "swb", "swc", "syc", "syr", "szl",
+ "ta", "tcy", "te", "tem", "teo", "ter", "tet", "tg",
+ "th", "ti", "tig", "tiv", "tk", "tkl", "tkr", "tl",
+ "tlh", "tli", "tly", "tmh", "tn", "to", "tog", "tpi",
+ "tr", "tru", "trv", "ts", "tsd", "tsi", "tt", "ttt",
+ "tum", "tvl", "tw", "twq", "ty", "tyv", "tzm",
+ "udm", "ug", "uga", "uk", "umb", "und", "ur", "uz",
+ "vai", "ve", "vec", "vep", "vi", "vls", "vmf", "vo",
+ "vot", "vro", "vun",
+ "wa", "wae", "wal", "war", "was", "wbp", "wo", "wuu",
+ "xal", "xh", "xmf", "xog",
+ "yao", "yap", "yav", "ybb", "yi", "yo", "yrl", "yue",
+ "za", "zap", "zbl", "zea", "zen", "zgh", "zh", "zu",
+ "zun", "zxx", "zza",
NULL,
"in", "iw", "ji", "jw", "sh", /* obsolete language codes */
NULL
* the two lists in LANGUAGES.
*/
/* Generated using org.unicode.cldr.icu.GenerateISO639LanguageTables */
-/* ISO639 table version is 20130531 */
+/* ISO639 table version is 20150505 */
+/* Subsequent hand addition of selected languages */
static const char * const LANGUAGES_3[] = {
- "aar", "abk", "ace", "ach", "ada", "ady", "ave", "afr",
- "afa", "afh", "agq", "ain", "aka", "akk", "ale", "alg",
- "alt", "amh", "arg", "ang", "anp", "apa", "ara", "arc",
- "arn", "arp", "art", "arw", "asm", "asa", "ast", "ath",
- "aus", "ava", "awa", "aym", "aze",
- "bak", "bad", "bai", "bal", "ban", "bas", "bat", "bax",
- "bbj", "bel", "bej", "bem", "ber", "bez", "bfd", "bul",
- "bih", "bho", "bis", "bik", "bin", "bkm", "bla", "bam",
- "ben", "bnt", "bod", "bre", "bra", "brx", "bos", "bss",
- "btk", "bua", "bug", "bum", "byn", "byv",
- "cat", "cad", "cai", "car", "cau", "cay", "cch", "che",
- "ceb", "cel", "cgg", "cha", "chb", "chg", "chk", "chm",
- "chn", "cho", "chp", "chr", "chy", "ckb", "cmc", "cos",
- "cop", "cpe", "cpf", "cpp", "cre", "crh", "crp", "ces",
- "csb", "chu", "cus", "chv", "cym",
- "dan", "dak", "dar", "dav", "day", "deu", "del", "den",
- "dgr", "din", "dje", "doi", "dra", "dsb", "dua", "dum",
- "div", "dyo", "dyu", "dzo", "dzg",
- "ebu", "ewe", "efi", "egy", "eka", "ell", "elx", "eng",
- "enm", "epo", "spa", "est", "eus", "ewo",
- "fas", "fan", "fat", "ful", "fin", "fil", "fiu", "fij",
- "fao", "fon", "fra", "frm", "fro", "frr", "frs", "fur",
- "fry",
- "gle", "gaa", "gay", "gba", "gla", "gem", "gez", "gil",
- "glg", "gmh", "grn", "goh", "gon", "gor", "got", "grb",
- "grc", "gsw", "guj", "guz", "glv", "gwi",
- "hau", "hai", "haw", "heb", "hin", "hil", "him", "hit",
- "hmn", "hmo", "hrv", "hsb", "hat", "hun", "hup", "hye",
- "her",
- "ina", "iba", "ibb", "ind", "ile", "ibo", "iii", "ijo",
- "ipk", "ilo", "inc", "ine", "inh", "ido", "ira", "iro",
- "isl", "ita", "iku",
- "jpn", "jbo", "jgo", "jmc", "jpr", "jrb", "jav",
- "kat", "kaa", "kab", "kac", "kaj", "kam", "kar", "kaw",
- "kbd", "kbl", "kcg", "kde", "kea", "kfo", "kon", "kha",
- "khi", "kho", "khq", "kik", "kua", "kaz", "kkj", "kal",
- "kln", "khm", "kmb", "kan", "kor", "kok", "kos", "kpe",
- "kau", "krc", "krl", "kro", "kru", "kas", "ksb", "ksf",
- "ksh", "kur", "kum", "kut", "kom", "cor", "kir",
- "lat", "lad", "lag", "lah", "lam", "ltz", "lez", "lug",
- "lim", "lkt", "lin", "lao", "lol", "loz", "lit", "lub",
- "lua", "lui", "lun", "luo", "lus", "luy", "lav",
- "mad", "maf", "mag", "mai", "mak", "man", "map", "mas",
- "mde", "mdf", "mdr", "men", "mer", "mfe", "mlg", "mga",
- "mgh", "mgo", "mah", "mri", "mic", "min", "mis", "mkd",
- "mkh", "mal", "mon", "mnc", "mni", "mno", "mol", "moh",
- "mos", "mar", "msa", "mlt", "mua", "mul", "mun", "mus",
- "mwl", "mwr", "mya", "mye", "myn", "myv",
- "nau", "nah", "nai", "nap", "naq", "nob", "nde", "nds",
- "nep", "new", "ndo", "nia", "nic", "niu", "nld", "nmg",
- "nno", "nnh", "nor", "nog", "non", "nqo", "nbl", "nso",
- "nub", "nus", "nav", "nwc", "nya", "nym", "nyn", "nyo",
- "nzi",
- "oci", "oji", "orm", "ori", "oss", "osa", "ota", "oto",
- "pan", "paa", "pag", "pal", "pam", "pap", "pau", "peo",
- "phi", "phn", "pli", "pol", "pon", "pra", "pro", "pus",
- "por",
- "que",
- "raj", "rap", "rar", "roh", "run", "ron", "roa", "rof",
- "rom", "rus", "rup", "kin", "rwk",
- "san", "sad", "sah", "sai", "sal", "sam", "saq", "sas",
- "sat", "sba", "sbp", "srd", "scn", "sco", "snd", "sme",
- "see", "seh", "sel", "sem", "ses", "sag", "sga", "sgn",
- "shi", "shn", "shu", "sin", "sid", "sio", "sit",
- "slk", "slv", "sla", "smo", "sma", "smi", "smj", "smn",
- "sms", "sna", "snk", "som", "sog", "son", "sqi", "srp",
- "srn", "srr", "ssw", "ssa", "ssy", "sot", "sun", "suk",
- "sus", "sux", "swe", "swa", "swb", "swc", "syc", "syr",
- "tam", "tai", "tel", "tem", "teo", "ter", "tet", "tgk",
- "tha", "tir", "tig", "tiv", "tuk", "tkl", "tgl", "tlh",
- "tli", "tmh", "tsn", "ton", "tog", "tpi", "tur", "trv",
- "tso", "tsi", "tat", "tum", "tup", "tut", "tvl", "twi",
- "twq", "tah", "tyv", "tzm",
- "udm", "uig", "uga", "ukr", "umb", "und", "urd", "uzb",
- "vai", "ven", "vie", "vol", "vot", "vun",
- "wln", "wae", "wak", "wal", "war", "was", "wen", "wol",
- "xal", "xho", "xog",
- "yao", "yap", "yav", "ybb", "yid", "yor", "ypk", "yue",
- "zha", "zap", "zbl", "zen", "zgh", "zho", "znd", "zul",
- "zun", "zxx", "zza",
+ "aar", "abk", "ace", "ach", "ada", "ady", "ave", "aeb",
+ "afr", "afh", "agq", "ain", "aka", "akk", "akz", "ale",
+ "aln", "alt", "amh", "arg", "ang", "anp", "ara", "arc",
+ "arn", "aro", "arp", "arq", "ars", "arw", "ary", "arz", "asm",
+ "asa", "ase", "ast", "ava", "avk", "awa", "aym", "aze",
+ "bak", "bal", "ban", "bar", "bas", "bax", "bbc", "bbj",
+ "bel", "bej", "bem", "bew", "bez", "bfd", "bfq", "bul",
+ "bgn", "bho", "bis", "bik", "bin", "bjn", "bkm", "bla",
+ "bam", "ben", "bod", "bpy", "bqi", "bre", "bra", "brh",
+ "brx", "bos", "bss", "bua", "bug", "bum", "byn", "byv",
+ "cat", "cad", "car", "cay", "cch", "ccp", "che", "ceb", "cgg",
+ "cha", "chb", "chg", "chk", "chm", "chn", "cho", "chp",
+ "chr", "chy", "ckb", "cos", "cop", "cps", "cre", "crh",
+ "ces", "csb", "chu", "chv", "cym",
+ "dan", "dak", "dar", "dav", "deu", "del", "den", "dgr",
+ "din", "dje", "doi", "dsb", "dtp", "dua", "dum", "div",
+ "dyo", "dyu", "dzo", "dzg",
+ "ebu", "ewe", "efi", "egl", "egy", "eka", "ell", "elx",
+ "eng", "enm", "epo", "spa", "esu", "est", "eus", "ewo",
+ "ext",
+ "fas", "fan", "fat", "ful", "fin", "fil", "fit", "fij",
+ "fao", "fon", "fra", "frc", "frm", "fro", "frp", "frr",
+ "frs", "fur", "fry",
+ "gle", "gaa", "gag", "gan", "gay", "gba", "gbz", "gla",
+ "gez", "gil", "glg", "glk", "gmh", "grn", "goh", "gom",
+ "gon", "gor", "got", "grb", "grc", "gsw", "guj", "guc",
+ "gur", "guz", "glv", "gwi",
+ "hau", "hai", "hak", "haw", "heb", "hin", "hif", "hil",
+ "hit", "hmn", "hmo", "hrv", "hsb", "hsn", "hat", "hun",
+ "hup", "hye", "her",
+ "ina", "iba", "ibb", "ind", "ile", "ibo", "iii", "ipk",
+ "ilo", "inh", "ido", "isl", "ita", "iku", "izh",
+ "jpn", "jam", "jbo", "jgo", "jmc", "jpr", "jrb", "jut",
+ "jav",
+ "kat", "kaa", "kab", "kac", "kaj", "kam", "kaw", "kbd",
+ "kbl", "kcg", "kde", "kea", "ken", "kfo", "kon", "kgp",
+ "kha", "kho", "khq", "khw", "kik", "kiu", "kua", "kaz",
+ "kkj", "kal", "kln", "khm", "kmb", "kan", "kor", "koi",
+ "kok", "kos", "kpe", "kau", "krc", "kri", "krj", "krl",
+ "kru", "kas", "ksb", "ksf", "ksh", "kur", "kum", "kut",
+ "kom", "cor", "kir",
+ "lat", "lad", "lag", "lah", "lam", "ltz", "lez", "lfn",
+ "lug", "lim", "lij", "liv", "lkt", "lmo", "lin", "lao",
+ "lol", "loz", "lrc", "lit", "ltg", "lub", "lua", "lui",
+ "lun", "luo", "lus", "luy", "lav", "lzh", "lzz",
+ "mad", "maf", "mag", "mai", "mak", "man", "mas", "mde",
+ "mdf", "mdh", "mdr", "men", "mer", "mfe", "mlg", "mga",
+ "mgh", "mgo", "mah", "mri", "mic", "min", "mis", "mkd",
+ "mal", "mon", "mnc", "mni", "mol",
+ "moh", "mos", "mar", "mrj",
+ "msa", "mlt", "mua", "mul", "mus", "mwl", "mwr", "mwv",
+ "mya", "mye", "myv", "mzn",
+ "nau", "nan", "nap", "naq", "nob", "nde", "nds", "nep",
+ "new", "ndo", "nia", "niu", "njo", "nld", "nmg", "nno",
+ "nnh", "nor", "nog", "non", "nov", "nqo", "nbl", "nso",
+ "nus", "nav", "nwc", "nya", "nym", "nyn", "nyo", "nzi",
+ "oci", "oji", "orm", "ori", "oss", "osa", "ota",
+ "pan", "pag", "pal", "pam", "pap", "pau", "pcd", "pdc",
+ "pdt", "peo", "pfl", "phn", "pli", "pol", "pms", "pnt",
+ "pon", "prg", "pro", "pus", "por",
+ "que", "quc", "qug",
+ "raj", "rap", "rar", "rgn", "rif", "roh", "run", "ron",
+ "rof", "rom", "rtm", "rus", "rue", "rug", "rup",
+ "kin", "rwk",
+ "san", "sad", "sah", "sam", "saq", "sas", "sat", "saz",
+ "sba", "sbp", "srd", "scn", "sco", "snd", "sdc", "sdh",
+ "sme", "see", "seh", "sei", "sel", "ses", "sag", "sga",
+ "sgs", "shi", "shn", "shu", "sin", "sid", "slk",
+ "slv", "sli", "sly", "smo", "sma", "smj", "smn", "sms",
+ "sna", "snk", "som", "sog", "sqi", "srp", "srn", "srr",
+ "ssw", "ssy", "sot", "stq", "sun", "suk", "sus", "sux",
+ "swe", "swa", "swb", "swc", "syc", "syr", "szl",
+ "tam", "tcy", "tel", "tem", "teo", "ter", "tet", "tgk",
+ "tha", "tir", "tig", "tiv", "tuk", "tkl", "tkr", "tgl",
+ "tlh", "tli", "tly", "tmh", "tsn", "ton", "tog", "tpi",
+ "tur", "tru", "trv", "tso", "tsd", "tsi", "tat", "ttt",
+ "tum", "tvl", "twi", "twq", "tah", "tyv", "tzm",
+ "udm", "uig", "uga", "ukr", "umb", "und", "urd", "uzb",
+ "vai", "ven", "vec", "vep", "vie", "vls", "vmf", "vol",
+ "vot", "vro", "vun",
+ "wln", "wae", "wal", "war", "was", "wbp", "wol", "wuu",
+ "xal", "xho", "xmf", "xog",
+ "yao", "yap", "yav", "ybb", "yid", "yor", "yrl", "yue",
+ "zha", "zap", "zbl", "zea", "zen", "zgh", "zho", "zul",
+ "zun", "zxx", "zza",
NULL,
/* "in", "iw", "ji", "jw", "sh", */
"ind", "heb", "yid", "jaw", "srp",
* http://www.iso.org/iso/en/prods-services/iso3166ma/03updates-on-iso-3166/nlv3e-rou.html
*/
static const char * const COUNTRIES[] = {
- "AD", "AE", "AF", "AG", "AI", "AL", "AM",
+ "AC", "AD", "AE", "AF", "AG", "AI", "AL", "AM",
"AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ",
"BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI",
"BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BV",
"BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG",
- "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR",
- "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK",
- "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER",
+ "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CP", "CR",
+ "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DG", "DJ", "DK",
+ "DM", "DO", "DZ", "EA", "EC", "EE", "EG", "EH", "ER",
"ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR",
"GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL",
"GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU",
"GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU",
- "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS",
+ "IC", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS",
"IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI",
"KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA",
"LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU",
"PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA",
"SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ",
"SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV",
- "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ",
+ "SX", "SY", "SZ", "TA", "TC", "TD", "TF", "TG", "TH", "TJ",
"TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV",
"TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ",
"VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF",
- "WS", "YE", "YT", "ZA", "ZM", "ZW",
+ "WS", "XK", "YE", "YT", "ZA", "ZM", "ZW",
NULL,
"AN", "BU", "CS", "FX", "RO", "SU", "TP", "YD", "YU", "ZR", /* obsolete country codes */
NULL
};
static const char* const REPLACEMENT_COUNTRIES[] = {
/* "AN", "BU", "CS", "DD", "DY", "FX", "HV", "NH", "RH", "SU", "TP", "UK", "VD", "YD", "YU", "ZR" */
- "CW", "MM", "RS", "DE", "BJ", "FR", "BF", "VU", "ZW", "RU", "TL", "GB", "VN", "YE", "RS", "CD", NULL, NULL /* replacement country codes */
+ "CW", "MM", "RS", "DE", "BJ", "FR", "BF", "VU", "ZW", "RU", "TL", "GB", "VN", "YE", "RS", "CD", NULL, NULL /* replacement country codes */
};
-
+
/**
* Table of 3-letter country codes.
*
* the two lists in COUNTRIES.
*/
static const char * const COUNTRIES_3[] = {
-/* "AD", "AE", "AF", "AG", "AI", "AL", "AM", */
- "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM",
+/* "AC", "AD", "AE", "AF", "AG", "AI", "AL", "AM", */
+ "ASC", "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM",
/* "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", */
"AGO", "ATA", "ARG", "ASM", "AUT", "AUS", "ABW", "ALA", "AZE",
/* "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", */
"BEN", "BLM", "BMU", "BRN", "BOL", "BES", "BRA", "BHS", "BTN", "BVT",
/* "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", */
"BWA", "BLR", "BLZ", "CAN", "CCK", "COD", "CAF", "COG",
-/* "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", */
- "CHE", "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CRI",
-/* "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK", */
- "CUB", "CPV", "CUW", "CXR", "CYP", "CZE", "DEU", "DJI", "DNK",
-/* "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", */
- "DMA", "DOM", "DZA", "ECU", "EST", "EGY", "ESH", "ERI",
+/* "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CP", "CR", */
+ "CHE", "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CPT", "CRI",
+/* "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DG", "DJ", "DK", */
+ "CUB", "CPV", "CUW", "CXR", "CYP", "CZE", "DEU", "DGA", "DJI", "DNK",
+/* "DM", "DO", "DZ", "EA", "EC", "EE", "EG", "EH", "ER", */
+ "DMA", "DOM", "DZA", "EA ", "ECU", "EST", "EGY", "ESH", "ERI", /* no valid 3-letter code for EA */
/* "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", */
"ESP", "ETH", "FIN", "FJI", "FLK", "FSM", "FRO", "FRA",
/* "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", */
"GMB", "GIN", "GLP", "GNQ", "GRC", "SGS", "GTM", "GUM",
/* "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", */
"GNB", "GUY", "HKG", "HMD", "HND", "HRV", "HTI", "HUN",
-/* "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS" */
- "IDN", "IRL", "ISR", "IMN", "IND", "IOT", "IRQ", "IRN", "ISL",
+/* "IC", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS" */
+ "IC ", "IDN", "IRL", "ISR", "IMN", "IND", "IOT", "IRQ", "IRN", "ISL", /* no valid 3-letter code for IC */
/* "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", */
"ITA", "JEY", "JAM", "JOR", "JPN", "KEN", "KGZ", "KHM", "KIR",
/* "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", */
"SLB", "SYC", "SDN", "SWE", "SGP", "SHN", "SVN", "SJM",
/* "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", */
"SVK", "SLE", "SMR", "SEN", "SOM", "SUR", "SSD", "STP", "SLV",
-/* "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", */
- "SXM", "SYR", "SWZ", "TCA", "TCD", "ATF", "TGO", "THA", "TJK",
+/* "SX", "SY", "SZ", "TA", "TC", "TD", "TF", "TG", "TH", "TJ", */
+ "SXM", "SYR", "SWZ", "TAA", "TCA", "TCD", "ATF", "TGO", "THA", "TJK",
/* "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", */
"TKL", "TLS", "TKM", "TUN", "TON", "TUR", "TTO", "TUV",
/* "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", */
"TWN", "TZA", "UKR", "UGA", "UMI", "USA", "URY", "UZB",
/* "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", */
"VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT", "WLF",
-/* "WS", "YE", "YT", "ZA", "ZM", "ZW", */
- "WSM", "YEM", "MYT", "ZAF", "ZMB", "ZWE",
+/* "WS", "XK", "YE", "YT", "ZA", "ZM", "ZW", */
+ "WSM", "XKK", "YEM", "MYT", "ZAF", "ZMB", "ZWE",
NULL,
/* "AN", "BU", "CS", "FX", "RO", "SU", "TP", "YD", "YU", "ZR" */
"ANT", "BUR", "SCG", "FXX", "ROM", "SUN", "TMP", "YMD", "YUG", "ZAR",
typedef struct CanonicalizationMap {
const char *id; /* input ID */
const char *canonicalID; /* canonicalized output ID */
- const char *keyword; /* keyword, or NULL if none */
- const char *value; /* keyword value, or NULL if kw==NULL */
} CanonicalizationMap;
/**
* different semantic kinds of transformations.
*/
static const CanonicalizationMap CANONICALIZE_MAP[] = {
- { "", "en_US_POSIX", NULL, NULL }, /* .NET name */
- { "c", "en_US_POSIX", NULL, NULL }, /* POSIX name */
- { "posix", "en_US_POSIX", NULL, NULL }, /* POSIX name (alias of C) */
- { "art_LOJBAN", "jbo", NULL, NULL }, /* registered name */
- { "az_AZ_CYRL", "az_Cyrl_AZ", NULL, NULL }, /* .NET name */
- { "az_AZ_LATN", "az_Latn_AZ", NULL, NULL }, /* .NET name */
- { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" },
- { "de__PHONEBOOK", "de", "collation", "phonebook" }, /* Old ICU name */
- { "de_AT_PREEURO", "de_AT", "currency", "ATS" },
- { "de_DE_PREEURO", "de_DE", "currency", "DEM" },
- { "de_LU_PREEURO", "de_LU", "currency", "LUF" },
- { "el_GR_PREEURO", "el_GR", "currency", "GRD" },
- { "en_BE_PREEURO", "en_BE", "currency", "BEF" },
- { "en_IE_PREEURO", "en_IE", "currency", "IEP" },
- { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
- { "es_ES_PREEURO", "es_ES", "currency", "ESP" },
- { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" },
- { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" },
- { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" },
- { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" },
- { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" },
- { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" },
- { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" },
- { "hi__DIRECT", "hi", "collation", "direct" }, /* Old ICU name */
- { "it_IT_PREEURO", "it_IT", "currency", "ITL" },
- { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" }, /* Old ICU name */
- { "nb_NO_NY", "nn_NO", NULL, NULL }, /* "markus said this was ok" :-) */
- { "nl_BE_PREEURO", "nl_BE", "currency", "BEF" },
- { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" },
- { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" },
- { "sr_SP_CYRL", "sr_Cyrl_RS", NULL, NULL }, /* .NET name */
- { "sr_SP_LATN", "sr_Latn_RS", NULL, NULL }, /* .NET name */
- { "sr_YU_CYRILLIC", "sr_Cyrl_RS", NULL, NULL }, /* Linux name */
- { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
- { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", NULL, NULL }, /* Linux name */
- { "uz_UZ_CYRL", "uz_Cyrl_UZ", NULL, NULL }, /* .NET name */
- { "uz_UZ_LATN", "uz_Latn_UZ", NULL, NULL }, /* .NET name */
- { "zh_CHS", "zh_Hans", NULL, NULL }, /* .NET name */
- { "zh_CHT", "zh_Hant", NULL, NULL }, /* .NET name */
- { "zh_GAN", "gan", NULL, NULL }, /* registered name */
- { "zh_GUOYU", "zh", NULL, NULL }, /* registered name */
- { "zh_HAKKA", "hak", NULL, NULL }, /* registered name */
- { "zh_MIN_NAN", "nan", NULL, NULL }, /* registered name */
- { "zh_WUU", "wuu", NULL, NULL }, /* registered name */
- { "zh_XIANG", "hsn", NULL, NULL }, /* registered name */
- { "zh_YUE", "yue", NULL, NULL }, /* registered name */
-};
-
-typedef struct VariantMap {
- const char *variant; /* input ID */
- const char *keyword; /* keyword, or NULL if none */
- const char *value; /* keyword value, or NULL if kw==NULL */
-} VariantMap;
-
-static const VariantMap VARIANT_MAP[] = {
- { "EURO", "currency", "EUR" },
- { "PINYIN", "collation", "pinyin" }, /* Solaris variant */
- { "STROKE", "collation", "stroke" } /* Solaris variant */
+ { "", "en_US_POSIX" }, /* .NET name */ // open ICU 64 deleted, we restore
+ { "c", "en_US_POSIX" }, /* POSIX name */ // open ICU 64 deleted, we restore
+ { "posix", "en_US_POSIX" }, /* POSIX name (alias of C) */ // open ICU 64 deleted, we restore
+ { "art_LOJBAN", "jbo" }, /* registered name */
+ { "hy__AREVELA", "hy" }, /* Registered IANA variant */
+ { "hy__AREVMDA", "hyw" }, /* Registered IANA variant */
+ { "zh_GAN", "gan" }, /* registered name */
+ { "zh_GUOYU", "zh" }, /* registered name */
+ { "zh_HAKKA", "hak" }, /* registered name */
+ { "zh_MIN_NAN", "nan" }, /* registered name */
+ { "zh_WUU", "wuu" }, /* registered name */
+ { "zh_XIANG", "hsn" }, /* registered name */
+ { "zh_YUE", "yue" }, /* registered name */
};
/* ### BCP47 Conversion *******************************************/
/* Test if the locale id has BCP47 u extension and does not have '@' */
#define _hasBCP47Extension(id) (id && uprv_strstr(id, "@") == NULL && getShortestSubtagLength(localeID) == 1)
/* Converts the BCP47 id to Unicode id. Does nothing to id if conversion fails */
-#define _ConvertBCP47(finalID, id, buffer, length,err) \
- if (uloc_forLanguageTag(id, buffer, length, NULL, err) <= 0 || U_FAILURE(*err)) { \
- finalID=id; \
- } else { \
- finalID=buffer; \
- }
+#define _ConvertBCP47(finalID, id, buffer, length,err) UPRV_BLOCK_MACRO_BEGIN { \
+ if (uloc_forLanguageTag(id, buffer, length, NULL, err) <= 0 || \
+ U_FAILURE(*err) || *err == U_STRING_NOT_TERMINATED_WARNING) { \
+ finalID=id; \
+ if (*err == U_STRING_NOT_TERMINATED_WARNING) { *err = U_BUFFER_OVERFLOW_ERROR; } \
+ } else { \
+ finalID=buffer; \
+ } \
+} UPRV_BLOCK_MACRO_END
/* Gets the size of the shortest subtag in the given localeID. */
static int32_t getShortestSubtagLength(const char *localeID) {
- int32_t localeIDLength = uprv_strlen(localeID);
+ int32_t localeIDLength = static_cast<int32_t>(uprv_strlen(localeID));
int32_t length = localeIDLength;
int32_t tmpLength = 0;
int32_t i;
}
/* ### Keywords **************************************************/
+#define UPRV_ISDIGIT(c) (((c) >= '0') && ((c) <= '9'))
+#define UPRV_ISALPHANUM(c) (uprv_isASCIILetter(c) || UPRV_ISDIGIT(c) )
+/* Punctuation/symbols allowed in legacy key values */
+#define UPRV_OK_VALUE_PUNCTUATION(c) ((c) == '_' || (c) == '-' || (c) == '+' || (c) == '/')
#define ULOC_KEYWORD_BUFFER_LEN 25
#define ULOC_MAX_NO_KEYWORDS 25
*/
static int32_t locale_canonKeywordName(char *buf, const char *keywordName, UErrorCode *status)
{
- int32_t i;
- int32_t keywordNameLen = (int32_t)uprv_strlen(keywordName);
-
- if(keywordNameLen >= ULOC_KEYWORD_BUFFER_LEN) {
- /* keyword name too long for internal buffer */
- *status = U_INTERNAL_PROGRAM_ERROR;
- return 0;
+ int32_t keywordNameLen = 0;
+
+ for (; *keywordName != 0; keywordName++) {
+ if (!UPRV_ISALPHANUM(*keywordName)) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */
+ return 0;
+ }
+ if (keywordNameLen < ULOC_KEYWORD_BUFFER_LEN - 1) {
+ buf[keywordNameLen++] = uprv_tolower(*keywordName);
+ } else {
+ /* keyword name too long for internal buffer */
+ *status = U_INTERNAL_PROGRAM_ERROR;
+ return 0;
+ }
}
-
- /* normalize the keyword name */
- for(i = 0; i < keywordNameLen; i++) {
- buf[i] = uprv_tolower(keywordName[i]);
+ if (keywordNameLen == 0) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name */
+ return 0;
}
- buf[i] = 0;
-
+ buf[keywordNameLen] = 0; /* terminate */
+
return keywordNameLen;
}
return uprv_strcmp(leftString, rightString);
}
-/**
- * Both addKeyword and addValue must already be in canonical form.
- * Either both addKeyword and addValue are NULL, or neither is NULL.
- * If they are not NULL they must be zero terminated.
- * If addKeyword is not NULL is must have length small enough to fit in KeywordStruct.keyword.
- */
static int32_t
_getKeywords(const char *localeID,
char prev,
char *keywords, int32_t keywordCapacity,
char *values, int32_t valuesCapacity, int32_t *valLen,
UBool valuesToo,
- const char* addKeyword,
- const char* addValue,
UErrorCode *status)
{
KeywordStruct keywordList[ULOC_MAX_NO_KEYWORDS];
-
+
int32_t maxKeywords = ULOC_MAX_NO_KEYWORDS;
int32_t numKeywords = 0;
const char* pos = localeID;
}
/* Premature end or zero-length value */
- if (!equalSign || equalSign == semicolon) {
+ if (!*equalSign || equalSign == semicolon) {
*status = U_INVALID_FORMAT_ERROR;
return 0;
}
}
} while(pos);
- /* Handle addKeyword/addValue. */
- if (addKeyword != NULL) {
- UBool duplicate = FALSE;
- U_ASSERT(addValue != NULL);
- /* Search for duplicate; if found, do nothing. Explicit keyword
- overrides addKeyword. */
- for (j=0; j<numKeywords; ++j) {
- if (uprv_strcmp(keywordList[j].keyword, addKeyword) == 0) {
- duplicate = TRUE;
- break;
- }
- }
- if (!duplicate) {
- if (numKeywords == maxKeywords) {
- *status = U_INTERNAL_PROGRAM_ERROR;
- return 0;
- }
- uprv_strcpy(keywordList[numKeywords].keyword, addKeyword);
- keywordList[numKeywords].keywordLen = (int32_t)uprv_strlen(addKeyword);
- keywordList[numKeywords].valueStart = addValue;
- keywordList[numKeywords].valueLen = (int32_t)uprv_strlen(addValue);
- ++numKeywords;
- }
- } else {
- U_ASSERT(addValue == NULL);
- }
-
/* now we have a list of keywords */
/* we need to sort it */
uprv_sortArray(keywordList, numKeywords, sizeof(KeywordStruct), compareKeywordStructs, NULL, FALSE, status);
-
+
/* Now construct the keyword part */
for(i = 0; i < numKeywords; i++) {
if(keywordsLen + keywordList[i].keywordLen + 1< keywordCapacity) {
}
keywordsLen += keywordList[i].keywordLen + 1;
if(valuesToo) {
- if(keywordsLen + keywordList[i].valueLen < keywordCapacity) {
+ if(keywordsLen + keywordList[i].valueLen <= keywordCapacity) {
uprv_strncpy(keywords+keywordsLen, keywordList[i].valueStart, keywordList[i].valueLen);
}
keywordsLen += keywordList[i].valueLen;
-
+
if(i < numKeywords - 1) {
- if(keywordsLen < keywordCapacity) {
+ if(keywordsLen < keywordCapacity) {
keywords[keywordsLen] = ';';
}
keywordsLen++;
*valLen = valuesLen;
}
}
- return u_terminateChars(keywords, keywordCapacity, keywordsLen, status);
+ return u_terminateChars(keywords, keywordCapacity, keywordsLen, status);
} else {
return 0;
}
UErrorCode *status) {
return _getKeywords(localeID, prev, keywords, keywordCapacity,
values, valuesCapacity, valLen, valuesToo,
- NULL, NULL, status);
+ status);
}
U_CAPI int32_t U_EXPORT2
const char* keywordName,
char* buffer, int32_t bufferCapacity,
UErrorCode* status)
-{
+{
+ if (buffer != nullptr) {
+ buffer[0] = '\0';
+ }
const char* startSearchHere = NULL;
const char* nextSeparator = NULL;
char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
- int32_t i = 0;
int32_t result = 0;
if(status && U_SUCCESS(*status) && localeID) {
char tempBuffer[ULOC_FULLNAME_CAPACITY];
const char* tmpLocaleID;
+ if (keywordName == NULL || keywordName[0] == 0) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+
+ locale_canonKeywordName(keywordNameBuffer, keywordName, status);
+ if(U_FAILURE(*status)) {
+ return 0;
+ }
+
if (_hasBCP47Extension(localeID)) {
_ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), status);
} else {
tmpLocaleID=localeID;
}
-
- startSearchHere = uprv_strchr(tmpLocaleID, '@'); /* TODO: REVISIT: shouldn't this be locale_getKeywordsStart ? */
+
+ startSearchHere = locale_getKeywordsStart(tmpLocaleID);
if(startSearchHere == NULL) {
/* no keywords, return at once */
return 0;
}
- locale_canonKeywordName(keywordNameBuffer, keywordName, status);
- if(U_FAILURE(*status)) {
- return 0;
- }
-
/* find the first keyword */
while(startSearchHere) {
- startSearchHere++;
- /* skip leading spaces (allowed?) */
+ const char* keyValueTail;
+ int32_t keyValueLen;
+
+ startSearchHere++; /* skip @ or ; */
+ nextSeparator = uprv_strchr(startSearchHere, '=');
+ if(!nextSeparator) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* key must have =value */
+ return 0;
+ }
+ /* strip leading & trailing spaces (TC decided to tolerate these) */
while(*startSearchHere == ' ') {
startSearchHere++;
}
- nextSeparator = uprv_strchr(startSearchHere, '=');
- /* need to normalize both keyword and keyword name */
- if(!nextSeparator) {
- break;
+ keyValueTail = nextSeparator;
+ while (keyValueTail > startSearchHere && *(keyValueTail-1) == ' ') {
+ keyValueTail--;
}
- if(nextSeparator - startSearchHere >= ULOC_KEYWORD_BUFFER_LEN) {
+ /* now keyValueTail points to first char after the keyName */
+ /* copy & normalize keyName from locale */
+ if (startSearchHere == keyValueTail) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name in passed-in locale */
+ return 0;
+ }
+ keyValueLen = 0;
+ while (startSearchHere < keyValueTail) {
+ if (!UPRV_ISALPHANUM(*startSearchHere)) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */
+ return 0;
+ }
+ if (keyValueLen < ULOC_KEYWORD_BUFFER_LEN - 1) {
+ localeKeywordNameBuffer[keyValueLen++] = uprv_tolower(*startSearchHere++);
+ } else {
/* keyword name too long for internal buffer */
*status = U_INTERNAL_PROGRAM_ERROR;
return 0;
+ }
}
- for(i = 0; i < nextSeparator - startSearchHere; i++) {
- localeKeywordNameBuffer[i] = uprv_tolower(startSearchHere[i]);
- }
- /* trim trailing spaces */
- while(startSearchHere[i-1] == ' ') {
- i--;
- U_ASSERT(i>=0);
- }
- localeKeywordNameBuffer[i] = 0;
-
+ localeKeywordNameBuffer[keyValueLen] = 0; /* terminate */
+
startSearchHere = uprv_strchr(nextSeparator, ';');
-
+
if(uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer) == 0) {
- nextSeparator++;
+ /* current entry matches the keyword. */
+ nextSeparator++; /* skip '=' */
+ /* First strip leading & trailing spaces (TC decided to tolerate these) */
while(*nextSeparator == ' ') {
- nextSeparator++;
+ nextSeparator++;
}
- /* we actually found the keyword. Copy the value */
- if(startSearchHere && startSearchHere - nextSeparator < bufferCapacity) {
- while(*(startSearchHere-1) == ' ') {
- startSearchHere--;
- }
- uprv_strncpy(buffer, nextSeparator, startSearchHere - nextSeparator);
- result = u_terminateChars(buffer, bufferCapacity, (int32_t)(startSearchHere - nextSeparator), status);
- } else if(!startSearchHere && (int32_t)uprv_strlen(nextSeparator) < bufferCapacity) { /* last item in string */
- i = (int32_t)uprv_strlen(nextSeparator);
- while(nextSeparator[i - 1] == ' ') {
- i--;
- }
- uprv_strncpy(buffer, nextSeparator, i);
- result = u_terminateChars(buffer, bufferCapacity, i, status);
- } else {
- /* give a bigger buffer, please */
- *status = U_BUFFER_OVERFLOW_ERROR;
- if(startSearchHere) {
- result = (int32_t)(startSearchHere - nextSeparator);
- } else {
- result = (int32_t)uprv_strlen(nextSeparator);
- }
+ keyValueTail = (startSearchHere)? startSearchHere: nextSeparator + uprv_strlen(nextSeparator);
+ while(keyValueTail > nextSeparator && *(keyValueTail-1) == ' ') {
+ keyValueTail--;
+ }
+ /* Now copy the value, but check well-formedness */
+ if (nextSeparator == keyValueTail) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty key value name in passed-in locale */
+ return 0;
}
+ keyValueLen = 0;
+ while (nextSeparator < keyValueTail) {
+ if (!UPRV_ISALPHANUM(*nextSeparator) && !UPRV_OK_VALUE_PUNCTUATION(*nextSeparator)) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed key value */
+ return 0;
+ }
+ if (keyValueLen < bufferCapacity) {
+ /* Should we lowercase value to return here? Tests expect as-is. */
+ buffer[keyValueLen++] = *nextSeparator++;
+ } else { /* keep advancing so we return correct length in case of overflow */
+ keyValueLen++;
+ nextSeparator++;
+ }
+ }
+ result = u_terminateChars(buffer, bufferCapacity, keyValueLen, status);
return result;
}
}
int32_t keywordValueLen;
int32_t bufLen;
int32_t needLen = 0;
- int32_t foundValueLen;
- int32_t keywordAtEnd = 0; /* is the keyword at the end of the string? */
char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
+ char keywordValueBuffer[ULOC_KEYWORDS_CAPACITY+1];
char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
- int32_t i = 0;
int32_t rc;
char* nextSeparator = NULL;
char* nextEqualsign = NULL;
char* startSearchHere = NULL;
char* keywordStart = NULL;
- char *insertHere = NULL;
- if(U_FAILURE(*status)) {
- return -1;
+ CharString updatedKeysAndValues;
+ int32_t updatedKeysAndValuesLen;
+ UBool handledInputKeyAndValue = FALSE;
+ char keyValuePrefix = '@';
+
+ if(U_FAILURE(*status)) {
+ return -1;
}
- if(bufferCapacity>1) {
- bufLen = (int32_t)uprv_strlen(buffer);
- } else {
+ if (keywordName == NULL || keywordName[0] == 0 || bufferCapacity <= 1) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
+ bufLen = (int32_t)uprv_strlen(buffer);
if(bufferCapacity<bufLen) {
/* The capacity is less than the length?! Is this NULL terminated? */
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
- if(keywordValue && !*keywordValue) {
- keywordValue = NULL;
- }
- if(keywordValue) {
- keywordValueLen = (int32_t)uprv_strlen(keywordValue);
- } else {
- keywordValueLen = 0;
- }
keywordNameLen = locale_canonKeywordName(keywordNameBuffer, keywordName, status);
if(U_FAILURE(*status)) {
return 0;
}
+
+ keywordValueLen = 0;
+ if(keywordValue) {
+ while (*keywordValue != 0) {
+ if (!UPRV_ISALPHANUM(*keywordValue) && !UPRV_OK_VALUE_PUNCTUATION(*keywordValue)) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed key value */
+ return 0;
+ }
+ if (keywordValueLen < ULOC_KEYWORDS_CAPACITY) {
+ /* Should we force lowercase in value to set? */
+ keywordValueBuffer[keywordValueLen++] = *keywordValue++;
+ } else {
+ /* keywordValue too long for internal buffer */
+ *status = U_INTERNAL_PROGRAM_ERROR;
+ return 0;
+ }
+ }
+ }
+ keywordValueBuffer[keywordValueLen] = 0; /* terminate */
+
startSearchHere = (char*)locale_getKeywordsStart(buffer);
if(startSearchHere == NULL || (startSearchHere[1]==0)) {
- if(!keywordValue) { /* no keywords = nothing to remove */
- return bufLen;
+ if(keywordValueLen == 0) { /* no keywords = nothing to remove */
+ return bufLen;
}
needLen = bufLen+1+keywordNameLen+1+keywordValueLen;
- if(startSearchHere) { /* had a single @ */
+ if(startSearchHere) { /* had a single @ */
needLen--; /* already had the @ */
/* startSearchHere points at the @ */
} else {
*status = U_BUFFER_OVERFLOW_ERROR;
return needLen; /* no change */
}
- *startSearchHere = '@';
- startSearchHere++;
+ *startSearchHere++ = '@';
uprv_strcpy(startSearchHere, keywordNameBuffer);
startSearchHere += keywordNameLen;
- *startSearchHere = '=';
- startSearchHere++;
- uprv_strcpy(startSearchHere, keywordValue);
- startSearchHere+=keywordValueLen;
+ *startSearchHere++ = '=';
+ uprv_strcpy(startSearchHere, keywordValueBuffer);
return needLen;
} /* end shortcut - no @ */
-
+
keywordStart = startSearchHere;
/* search for keyword */
while(keywordStart) {
- keywordStart++;
- /* skip leading spaces (allowed?) */
+ const char* keyValueTail;
+ int32_t keyValueLen;
+
+ keywordStart++; /* skip @ or ; */
+ nextEqualsign = uprv_strchr(keywordStart, '=');
+ if (!nextEqualsign) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* key must have =value */
+ return 0;
+ }
+ /* strip leading & trailing spaces (TC decided to tolerate these) */
while(*keywordStart == ' ') {
keywordStart++;
}
- nextEqualsign = uprv_strchr(keywordStart, '=');
- /* need to normalize both keyword and keyword name */
- if(!nextEqualsign) {
- break;
+ keyValueTail = nextEqualsign;
+ while (keyValueTail > keywordStart && *(keyValueTail-1) == ' ') {
+ keyValueTail--;
}
- if(nextEqualsign - keywordStart >= ULOC_KEYWORD_BUFFER_LEN) {
- /* keyword name too long for internal buffer */
- *status = U_INTERNAL_PROGRAM_ERROR;
+ /* now keyValueTail points to first char after the keyName */
+ /* copy & normalize keyName from locale */
+ if (keywordStart == keyValueTail) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name in passed-in locale */
return 0;
}
- for(i = 0; i < nextEqualsign - keywordStart; i++) {
- localeKeywordNameBuffer[i] = uprv_tolower(keywordStart[i]);
- }
- /* trim trailing spaces */
- while(keywordStart[i-1] == ' ') {
- i--;
+ keyValueLen = 0;
+ while (keywordStart < keyValueTail) {
+ if (!UPRV_ISALPHANUM(*keywordStart)) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */
+ return 0;
+ }
+ if (keyValueLen < ULOC_KEYWORD_BUFFER_LEN - 1) {
+ localeKeywordNameBuffer[keyValueLen++] = uprv_tolower(*keywordStart++);
+ } else {
+ /* keyword name too long for internal buffer */
+ *status = U_INTERNAL_PROGRAM_ERROR;
+ return 0;
+ }
}
- U_ASSERT(i>=0 && i<ULOC_KEYWORD_BUFFER_LEN);
- localeKeywordNameBuffer[i] = 0;
+ localeKeywordNameBuffer[keyValueLen] = 0; /* terminate */
nextSeparator = uprv_strchr(nextEqualsign, ';');
+
+ /* start processing the value part */
+ nextEqualsign++; /* skip '=' */
+ /* First strip leading & trailing spaces (TC decided to tolerate these) */
+ while(*nextEqualsign == ' ') {
+ nextEqualsign++;
+ }
+ keyValueTail = (nextSeparator)? nextSeparator: nextEqualsign + uprv_strlen(nextEqualsign);
+ while(keyValueTail > nextEqualsign && *(keyValueTail-1) == ' ') {
+ keyValueTail--;
+ }
+ if (nextEqualsign == keyValueTail) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty key value in passed-in locale */
+ return 0;
+ }
+
rc = uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer);
if(rc == 0) {
- nextEqualsign++;
- while(*nextEqualsign == ' ') {
- nextEqualsign++;
- }
- /* we actually found the keyword. Change the value */
- if (nextSeparator) {
- keywordAtEnd = 0;
- foundValueLen = (int32_t)(nextSeparator - nextEqualsign);
- } else {
- keywordAtEnd = 1;
- foundValueLen = (int32_t)uprv_strlen(nextEqualsign);
- }
- if(keywordValue) { /* adding a value - not removing */
- if(foundValueLen == keywordValueLen) {
- uprv_strncpy(nextEqualsign, keywordValue, keywordValueLen);
- return bufLen; /* no change in size */
- } else if(foundValueLen > keywordValueLen) {
- int32_t delta = foundValueLen - keywordValueLen;
- if(nextSeparator) { /* RH side */
- uprv_memmove(nextSeparator - delta, nextSeparator, bufLen-(nextSeparator-buffer));
- }
- uprv_strncpy(nextEqualsign, keywordValue, keywordValueLen);
- bufLen -= delta;
- buffer[bufLen]=0;
- return bufLen;
- } else { /* FVL < KVL */
- int32_t delta = keywordValueLen - foundValueLen;
- if((bufLen+delta) >= bufferCapacity) {
- *status = U_BUFFER_OVERFLOW_ERROR;
- return bufLen+delta;
- }
- if(nextSeparator) { /* RH side */
- uprv_memmove(nextSeparator+delta,nextSeparator, bufLen-(nextSeparator-buffer));
- }
- uprv_strncpy(nextEqualsign, keywordValue, keywordValueLen);
- bufLen += delta;
- buffer[bufLen]=0;
- return bufLen;
- }
- } else { /* removing a keyword */
- if(keywordAtEnd) {
- /* zero out the ';' or '@' just before startSearchhere */
- keywordStart[-1] = 0;
- return (int32_t)((keywordStart-buffer)-1); /* (string length without keyword) minus separator */
- } else {
- uprv_memmove(keywordStart, nextSeparator+1, bufLen-((nextSeparator+1)-buffer));
- keywordStart[bufLen-((nextSeparator+1)-buffer)]=0;
- return (int32_t)(bufLen-((nextSeparator+1)-keywordStart));
- }
+ /* Current entry matches the input keyword. Update the entry */
+ if(keywordValueLen > 0) { /* updating a value */
+ updatedKeysAndValues.append(keyValuePrefix, *status);
+ keyValuePrefix = ';'; /* for any subsequent key-value pair */
+ updatedKeysAndValues.append(keywordNameBuffer, keywordNameLen, *status);
+ updatedKeysAndValues.append('=', *status);
+ updatedKeysAndValues.append(keywordValueBuffer, keywordValueLen, *status);
+ } /* else removing this entry, don't emit anything */
+ handledInputKeyAndValue = TRUE;
+ } else {
+ /* input keyword sorts earlier than current entry, add before current entry */
+ if (rc < 0 && keywordValueLen > 0 && !handledInputKeyAndValue) {
+ /* insert new entry at this location */
+ updatedKeysAndValues.append(keyValuePrefix, *status);
+ keyValuePrefix = ';'; /* for any subsequent key-value pair */
+ updatedKeysAndValues.append(keywordNameBuffer, keywordNameLen, *status);
+ updatedKeysAndValues.append('=', *status);
+ updatedKeysAndValues.append(keywordValueBuffer, keywordValueLen, *status);
+ handledInputKeyAndValue = TRUE;
}
- } else if(rc<0){ /* end match keyword */
- /* could insert at this location. */
- insertHere = keywordStart;
+ /* copy the current entry */
+ updatedKeysAndValues.append(keyValuePrefix, *status);
+ keyValuePrefix = ';'; /* for any subsequent key-value pair */
+ updatedKeysAndValues.append(localeKeywordNameBuffer, keyValueLen, *status);
+ updatedKeysAndValues.append('=', *status);
+ updatedKeysAndValues.append(nextEqualsign, static_cast<int32_t>(keyValueTail-nextEqualsign), *status);
+ }
+ if (!nextSeparator && keywordValueLen > 0 && !handledInputKeyAndValue) {
+ /* append new entry at the end, it sorts later than existing entries */
+ updatedKeysAndValues.append(keyValuePrefix, *status);
+ /* skip keyValuePrefix update, no subsequent key-value pair */
+ updatedKeysAndValues.append(keywordNameBuffer, keywordNameLen, *status);
+ updatedKeysAndValues.append('=', *status);
+ updatedKeysAndValues.append(keywordValueBuffer, keywordValueLen, *status);
+ handledInputKeyAndValue = TRUE;
}
keywordStart = nextSeparator;
} /* end loop searching */
-
- if(!keywordValue) {
- return bufLen; /* removal of non-extant keyword - no change */
- }
- /* we know there is at least one keyword. */
- needLen = bufLen+1+keywordNameLen+1+keywordValueLen;
+ /* Any error from updatedKeysAndValues.append above would be internal and not due to
+ * problems with the passed-in locale. So if we did encounter problems with the
+ * passed-in locale above, those errors took precedence and overrode any error
+ * status from updatedKeysAndValues.append, and also caused a return of 0. If there
+ * are errors here they are from updatedKeysAndValues.append; they do cause an
+ * error return but the passed-in locale is unmodified and the original bufLen is
+ * returned.
+ */
+ if (!handledInputKeyAndValue || U_FAILURE(*status)) {
+ /* if input key/value specified removal of a keyword not present in locale, or
+ * there was an error in CharString.append, leave original locale alone. */
+ return bufLen;
+ }
+
+ updatedKeysAndValuesLen = updatedKeysAndValues.length();
+ /* needLen = length of the part before '@' + length of updated key-value part including '@' */
+ needLen = (int32_t)(startSearchHere - buffer) + updatedKeysAndValuesLen;
if(needLen >= bufferCapacity) {
*status = U_BUFFER_OVERFLOW_ERROR;
return needLen; /* no change */
}
-
- if(insertHere) {
- uprv_memmove(insertHere+(1+keywordNameLen+1+keywordValueLen), insertHere, bufLen-(insertHere-buffer));
- keywordStart = insertHere;
- } else {
- keywordStart = buffer+bufLen;
- *keywordStart = ';';
- keywordStart++;
- }
- uprv_strncpy(keywordStart, keywordNameBuffer, keywordNameLen);
- keywordStart += keywordNameLen;
- *keywordStart = '=';
- keywordStart++;
- uprv_strncpy(keywordStart, keywordValue, keywordValueLen); /* terminates. */
- keywordStart+=keywordValueLen;
- if(insertHere) {
- *keywordStart = ';';
- keywordStart++;
+ if (updatedKeysAndValuesLen > 0) {
+ uprv_strncpy(startSearchHere, updatedKeysAndValues.data(), updatedKeysAndValuesLen);
}
buffer[needLen]=0;
return needLen;
*/
#define _isTerminator(a) ((a==0)||(a=='.')||(a=='@'))
-static char* _strnchr(const char* str, int32_t len, char c) {
- U_ASSERT(str != 0 && len >= 0);
- while (len-- != 0) {
- char d = *str;
- if (d == c) {
- return (char*) str;
- } else if (d == 0) {
- break;
- }
- ++str;
- }
- return NULL;
-}
-
/**
* Lookup 'key' in the array 'list'. The array 'list' should contain
* a NULL entry, followed by more entries, and a second NULL entry.
}
}
-U_CFUNC const char*
+U_CFUNC const char*
uloc_getCurrentCountryID(const char* oldID){
int32_t offset = _findIndex(DEPRECATED_COUNTRIES, oldID);
if (offset >= 0) {
}
return oldID;
}
-U_CFUNC const char*
+U_CFUNC const char*
uloc_getCurrentLanguageID(const char* oldID){
int32_t offset = _findIndex(DEPRECATED_LANGUAGES, oldID);
if (offset >= 0) {
return REPLACEMENT_LANGUAGES[offset];
}
- return oldID;
+ return oldID;
}
/*
* the internal functions _getLanguage(), _getCountry(), _getVariant()
i+=2;
localeID+=2;
}
-
+
/* copy the language as far as possible and count its length */
while(!_isTerminator(*localeID) && !_isIDSeparator(*localeID)) {
if(i<languageCapacity) {
localeID++;
}
}
-
+
return i;
}
return _getVariantEx(localeID, prev, variant, variantCapacity, FALSE);
}
-/**
- * Delete ALL instances of a variant from the given list of one or
- * more variants. Example: "FOO_EURO_BAR_EURO" => "FOO_BAR".
- * @param variants the source string of one or more variants,
- * separated by '_'. This will be MODIFIED IN PLACE. Not zero
- * terminated; if it is, trailing zero will NOT be maintained.
- * @param variantsLen length of variants
- * @param toDelete variant to delete, without separators, e.g. "EURO"
- * or "PREEURO"; not zero terminated
- * @param toDeleteLen length of toDelete
- * @return number of characters deleted from variants
- */
-static int32_t
-_deleteVariant(char* variants, int32_t variantsLen,
- const char* toDelete, int32_t toDeleteLen)
-{
- int32_t delta = 0; /* number of chars deleted */
- for (;;) {
- UBool flag = FALSE;
- if (variantsLen < toDeleteLen) {
- return delta;
- }
- if (uprv_strncmp(variants, toDelete, toDeleteLen) == 0 &&
- (variantsLen == toDeleteLen ||
- (flag=(variants[toDeleteLen] == '_'))))
- {
- int32_t d = toDeleteLen + (flag?1:0);
- variantsLen -= d;
- delta += d;
- if (variantsLen > 0) {
- uprv_memmove(variants, variants+d, variantsLen);
- }
- } else {
- char* p = _strnchr(variants, variantsLen, '_');
- if (p == NULL) {
- return delta;
- }
- ++p;
- variantsLen -= (int32_t)(p - variants);
- variants = p;
- }
- }
-}
-
/* Keyword enumeration */
typedef struct UKeywordsContext {
char* current;
} UKeywordsContext;
+U_CDECL_BEGIN
+
static void U_CALLCONV
uloc_kw_closeKeywords(UEnumeration *enumerator) {
uprv_free(((UKeywordsContext *)enumerator->context)->keywords);
return result;
}
-static const char* U_CALLCONV
+static const char * U_CALLCONV
uloc_kw_nextKeyword(UEnumeration* en,
int32_t* resultLength,
UErrorCode* /*status*/) {
return result;
}
-static void U_CALLCONV
-uloc_kw_resetKeywords(UEnumeration* en,
+static void U_CALLCONV
+uloc_kw_resetKeywords(UEnumeration* en,
UErrorCode* /*status*/) {
((UKeywordsContext *)en->context)->current = ((UKeywordsContext *)en->context)->keywords;
}
+U_CDECL_END
+
+
static const UEnumeration gKeywordsEnum = {
NULL,
NULL,
U_CAPI UEnumeration* U_EXPORT2
uloc_openKeywordList(const char *keywordList, int32_t keywordListSize, UErrorCode* status)
{
- UKeywordsContext *myContext = NULL;
- UEnumeration *result = NULL;
+ LocalMemory<UKeywordsContext> myContext;
+ LocalMemory<UEnumeration> result;
- if(U_FAILURE(*status)) {
- return NULL;
+ if (U_FAILURE(*status)) {
+ return nullptr;
}
- result = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
- /* Null pointer test */
- if (result == NULL) {
+ myContext.adoptInstead(static_cast<UKeywordsContext *>(uprv_malloc(sizeof(UKeywordsContext))));
+ result.adoptInstead(static_cast<UEnumeration *>(uprv_malloc(sizeof(UEnumeration))));
+ if (myContext.isNull() || result.isNull()) {
*status = U_MEMORY_ALLOCATION_ERROR;
- return NULL;
+ return nullptr;
}
- uprv_memcpy(result, &gKeywordsEnum, sizeof(UEnumeration));
- myContext = static_cast<UKeywordsContext *>(uprv_malloc(sizeof(UKeywordsContext)));
- if (myContext == NULL) {
+ uprv_memcpy(result.getAlias(), &gKeywordsEnum, sizeof(UEnumeration));
+ myContext->keywords = static_cast<char *>(uprv_malloc(keywordListSize+1));
+ if (myContext->keywords == nullptr) {
*status = U_MEMORY_ALLOCATION_ERROR;
- uprv_free(result);
- return NULL;
+ return nullptr;
}
- myContext->keywords = (char *)uprv_malloc(keywordListSize+1);
uprv_memcpy(myContext->keywords, keywordList, keywordListSize);
myContext->keywords[keywordListSize] = 0;
myContext->current = myContext->keywords;
- result->context = myContext;
- return result;
+ result->context = myContext.orphan();
+ return result.orphan();
}
U_CAPI UEnumeration* U_EXPORT2
uloc_openKeywords(const char* localeID,
- UErrorCode* status)
+ UErrorCode* status)
{
int32_t i=0;
char keywords[256];
if(status==NULL || U_FAILURE(*status)) {
return 0;
}
-
+
if (_hasBCP47Extension(localeID)) {
_ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), status);
} else {
#define OPTION_SET(options, mask) ((options & mask) != 0)
static const char i_default[] = {'i', '-', 'd', 'e', 'f', 'a', 'u', 'l', 't'};
-#define I_DEFAULT_LENGTH (sizeof i_default / sizeof i_default[0])
+#define I_DEFAULT_LENGTH UPRV_LENGTHOF(i_default)
/**
* Canonicalize the given localeID, to level 1 or to level 2,
const char* tmpLocaleID;
const char* keywordAssign = NULL;
const char* separatorIndicator = NULL;
- const char* addKeyword = NULL;
- const char* addValue = NULL;
char* name;
char* variant = NULL; /* pointer into name, or NULL */
if (U_FAILURE(*err)) {
return 0;
}
-
+
if (_hasBCP47Extension(localeID)) {
_ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), err);
} else {
if(len == I_DEFAULT_LENGTH && uprv_strncmp(origLocaleID, i_default, len) == 0) {
const char *d = uloc_getDefault();
-
+
len = (int32_t)uprv_strlen(d);
if (name != NULL) {
- uprv_strncpy(name, d, len);
+ uprv_memcpy(name, d, len);
}
} else if(_isIDSeparator(*tmpLocaleID)) {
const char *scriptID;
}
}
- /* Handle generic variants first */
- if (variant) {
- for (j=0; j<(int32_t)(sizeof(VARIANT_MAP)/sizeof(VARIANT_MAP[0])); j++) {
- const char* variantToCompare = VARIANT_MAP[j].variant;
- int32_t n = (int32_t)uprv_strlen(variantToCompare);
- int32_t variantLen = _deleteVariant(variant, uprv_min(variantSize, (nameCapacity-len)), variantToCompare, n);
- len -= variantLen;
- if (variantLen > 0) {
- if (len > 0 && name[len-1] == '_') { /* delete trailing '_' */
- --len;
- }
- addKeyword = VARIANT_MAP[j].keyword;
- addValue = VARIANT_MAP[j].value;
- break;
- }
- }
- if (len > 0 && len <= nameCapacity && name[len-1] == '_') { /* delete trailing '_' */
- --len;
- }
- }
-
/* Look up the ID in the canonicalization map */
- for (j=0; j<(int32_t)(sizeof(CANONICALIZE_MAP)/sizeof(CANONICALIZE_MAP[0])); j++) {
+ for (j=0; j<UPRV_LENGTHOF(CANONICALIZE_MAP); j++) {
const char* id = CANONICALIZE_MAP[j].id;
int32_t n = (int32_t)uprv_strlen(id);
if (len == n && uprv_strncmp(name, id, n) == 0) {
break; /* Don't remap "" if keywords present */
}
len = _copyCount(name, nameCapacity, CANONICALIZE_MAP[j].canonicalID);
- if (CANONICALIZE_MAP[j].keyword) {
- addKeyword = CANONICALIZE_MAP[j].keyword;
- addValue = CANONICALIZE_MAP[j].value;
- }
break;
}
}
++len;
++fieldCount;
len += _getKeywords(tmpLocaleID+1, '@', (len<nameCapacity ? name+len : NULL), nameCapacity-len,
- NULL, 0, NULL, TRUE, addKeyword, addValue, err);
- } else if (addKeyword != NULL) {
- U_ASSERT(addValue != NULL && len < nameCapacity);
- /* inelegant but works -- later make _getKeywords do this? */
- len += _copyCount(name+len, nameCapacity-len, "@");
- len += _copyCount(name+len, nameCapacity-len, addKeyword);
- len += _copyCount(name+len, nameCapacity-len, "=");
- len += _copyCount(name+len, nameCapacity-len, addValue);
+ NULL, 0, NULL, TRUE, err);
}
}
{
const char *lastUnderscore;
int32_t i;
-
+
if (U_FAILURE(*err))
return 0;
-
+
if (localeID == NULL)
localeID = uloc_getDefault();
if(i>0 && parent != localeID) {
uprv_memcpy(parent, localeID, uprv_min(i, parentCapacity));
}
+
return u_terminateChars(parent, parentCapacity, i, err);
}
if (err==NULL || U_FAILURE(*err)) {
return 0;
}
-
+
if(localeID==NULL) {
localeID=uloc_getDefault();
}
uloc_getCountry(const char* localeID,
char* country,
int32_t countryCapacity,
- UErrorCode* err)
+ UErrorCode* err)
{
int32_t i=0;
uloc_getVariant(const char* localeID,
char* variant,
int32_t variantCapacity,
- UErrorCode* err)
+ UErrorCode* err)
{
char tempBuffer[ULOC_FULLNAME_CAPACITY];
const char* tmpLocaleID;
int32_t i=0;
-
+
if(err==NULL || U_FAILURE(*err)) {
return 0;
}
-
+
if (_hasBCP47Extension(localeID)) {
_ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), err);
} else {
}
tmpLocaleID=localeID;
}
-
+
/* Skip the language */
ulocimp_getLanguage(tmpLocaleID, NULL, 0, &tmpLocaleID);
if(_isIDSeparator(*tmpLocaleID)) {
}
}
}
-
+
/* removed by weiv. We don't want to handle POSIX variants anymore. Use canonicalization function */
/* if we do not have a variant tag yet then try a POSIX variant after '@' */
/*
uloc_getName(const char* localeID,
char* name,
int32_t nameCapacity,
- UErrorCode* err)
+ UErrorCode* err)
{
return _canonicalize(localeID, name, nameCapacity, 0, err);
}
uloc_getBaseName(const char* localeID,
char* name,
int32_t nameCapacity,
- UErrorCode* err)
+ UErrorCode* err)
{
return _canonicalize(localeID, name, nameCapacity, _ULOC_STRIP_KEYWORDS, err);
}
uloc_canonicalize(const char* localeID,
char* name,
int32_t nameCapacity,
- UErrorCode* err)
+ UErrorCode* err)
{
return _canonicalize(localeID, name, nameCapacity, _ULOC_CANONICALIZE, err);
}
-
+
U_CAPI const char* U_EXPORT2
-uloc_getISO3Language(const char* localeID)
+uloc_getISO3Language(const char* localeID)
{
int16_t offset;
char lang[ULOC_LANG_CAPACITY];
UErrorCode err = U_ZERO_ERROR;
-
+
if (localeID == NULL)
{
localeID = uloc_getDefault();
}
U_CAPI const char* U_EXPORT2
-uloc_getISO3Country(const char* localeID)
+uloc_getISO3Country(const char* localeID)
{
int16_t offset;
char cntry[ULOC_LANG_CAPACITY];
UErrorCode err = U_ZERO_ERROR;
-
+
if (localeID == NULL)
{
localeID = uloc_getDefault();
offset = _findIndex(COUNTRIES, cntry);
if (offset < 0)
return "";
-
+
return COUNTRIES_3[offset];
}
U_CAPI uint32_t U_EXPORT2
-uloc_getLCID(const char* localeID)
+uloc_getLCID(const char* localeID)
{
UErrorCode status = U_ZERO_ERROR;
char langID[ULOC_FULLNAME_CAPACITY];
+ uint32_t lcid = 0;
- uloc_getLanguage(localeID, langID, sizeof(langID), &status);
+ /* Check for incomplete id. */
+ if (!localeID || uprv_strlen(localeID) < 2) {
+ return 0;
+ }
+
+ // First, attempt Windows platform lookup if available, but fall
+ // through to catch any special cases (ICU vs Windows name differences).
+ lcid = uprv_convertToLCIDPlatform(localeID, &status);
if (U_FAILURE(status)) {
return 0;
}
+ if (lcid > 0) {
+ // Windows found an LCID, return that
+ return lcid;
+ }
+
+ uloc_getLanguage(localeID, langID, sizeof(langID), &status);
+ if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
+ return 0;
+ }
if (uprv_strchr(localeID, '@')) {
// uprv_convertToLCID does not support keywords other than collation.
char tmpLocaleID[ULOC_FULLNAME_CAPACITY];
len = uloc_getKeywordValue(localeID, "collation", collVal,
- sizeof(collVal)/sizeof(collVal[0]) - 1, &status);
+ UPRV_LENGTHOF(collVal) - 1, &status);
if (U_SUCCESS(status) && len > 0) {
collVal[len] = 0;
len = uloc_getBaseName(localeID, tmpLocaleID,
- sizeof(tmpLocaleID)/sizeof(tmpLocaleID[0]) - 1, &status);
+ UPRV_LENGTHOF(tmpLocaleID) - 1, &status);
- if (U_SUCCESS(status)) {
+ if (U_SUCCESS(status) && len > 0) {
tmpLocaleID[len] = 0;
len = uloc_setKeywordValue("collation", collVal, tmpLocaleID,
- sizeof(tmpLocaleID)/sizeof(tmpLocaleID[0]) - len - 1, &status);
+ UPRV_LENGTHOF(tmpLocaleID) - len - 1, &status);
- if (U_SUCCESS(status)) {
+ if (U_SUCCESS(status) && len > 0) {
tmpLocaleID[len] = 0;
return uprv_convertToLCID(langID, tmpLocaleID, &status);
}
U_CAPI void U_EXPORT2
uloc_setDefault(const char* newDefaultLocale,
- UErrorCode* err)
+ UErrorCode* err)
{
if (U_FAILURE(*err))
return;
/* the error code isn't currently used for anything by this function*/
-
+
/* propagate change to C++ */
locale_set_default(newDefaultLocale);
}
* terminated with a null pointer.
*/
U_CAPI const char* const* U_EXPORT2
-uloc_getISOLanguages()
+uloc_getISOLanguages()
{
return LANGUAGES;
}
* terminated with a null pointer.
*/
U_CAPI const char* const* U_EXPORT2
-uloc_getISOCountries()
+uloc_getISOCountries()
{
return COUNTRIES;
}
}
}
-typedef struct {
+typedef struct {
float q;
int32_t dummy; /* to avoid uninitialized memory copy from qsort */
- char *locale;
+ char locale[ULOC_FULLNAME_CAPACITY+1];
} _acceptLangItem;
static int32_t U_CALLCONV
}
#if defined(ULOC_DEBUG)
- /* fprintf(stderr, "a:[%s:%g], b:[%s:%g] -> %d\n",
- aa->locale, aa->q,
+ /* fprintf(stderr, "a:[%s:%g], b:[%s:%g] -> %d\n",
+ aa->locale, aa->q,
bb->locale, bb->q,
rc);*/
#endif
return rc;
}
-/*
+/*
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
*/
UEnumeration* availableLocales,
UErrorCode *status)
{
- _acceptLangItem *j;
- _acceptLangItem smallBuffer[30];
- char **strs;
+ MaybeStackArray<_acceptLangItem, 4> items; // Struct for collecting items.
char tmp[ULOC_FULLNAME_CAPACITY +1];
int32_t n = 0;
const char *itemEnd;
int32_t res;
int32_t i;
int32_t l = (int32_t)uprv_strlen(httpAcceptLanguage);
- int32_t jSize;
- char *tempstr; /* Use for null pointer check */
- j = smallBuffer;
- jSize = sizeof(smallBuffer)/sizeof(smallBuffer[0]);
if(U_FAILURE(*status)) {
return -1;
}
if(!itemEnd) {
itemEnd = httpAcceptLanguage+l; /* end of string */
}
- if(paramEnd && paramEnd<itemEnd) {
+ if(paramEnd && paramEnd<itemEnd) {
/* semicolon (;) is closer than end (,) */
t = paramEnd+1;
if(*t=='q') {
while(isspace(*t)) {
t++;
}
- j[n].q = (float)_uloc_strtod(t,NULL);
+ items[n].q = (float)_uloc_strtod(t,NULL);
} else {
/* no semicolon - it's 1.0 */
- j[n].q = 1.0f;
+ items[n].q = 1.0f;
paramEnd = itemEnd;
}
- j[n].dummy=0;
+ items[n].dummy=0;
/* eat spaces prior to semi */
for(t=(paramEnd-1);(paramEnd>s)&&isspace(*t);t--)
;
- /* Check for null pointer from uprv_strndup */
- tempstr = uprv_strndup(s,(int32_t)((t+1)-s));
- if (tempstr == NULL) {
- *status = U_MEMORY_ALLOCATION_ERROR;
- return -1;
- }
- j[n].locale = tempstr;
- uloc_canonicalize(j[n].locale,tmp,sizeof(tmp)/sizeof(tmp[0]),status);
- if(strcmp(j[n].locale,tmp)) {
- uprv_free(j[n].locale);
- j[n].locale=uprv_strdup(tmp);
+ int32_t slen = static_cast<int32_t>(((t+1)-s));
+ if(slen > ULOC_FULLNAME_CAPACITY) {
+ *status = U_BUFFER_OVERFLOW_ERROR;
+ return -1; // too big
+ }
+ uprv_strncpy(items[n].locale, s, slen);
+ items[n].locale[slen]=0; // terminate
+ int32_t clen = uloc_canonicalize(items[n].locale, tmp, UPRV_LENGTHOF(tmp)-1, status);
+ if(U_FAILURE(*status)) return -1;
+ if((clen!=slen) || (uprv_strncmp(items[n].locale, tmp, slen))) {
+ // canonicalization had an effect- copy back
+ uprv_strncpy(items[n].locale, tmp, clen);
+ items[n].locale[clen] = 0; // terminate
}
#if defined(ULOC_DEBUG)
/*fprintf(stderr,"%d: s <%s> q <%g>\n", n, j[n].locale, j[n].q);*/
while(*s==',') { /* eat duplicate commas */
s++;
}
- if(n>=jSize) {
- if(j==smallBuffer) { /* overflowed the small buffer. */
- j = static_cast<_acceptLangItem *>(uprv_malloc(sizeof(j[0])*(jSize*2)));
- if(j!=NULL) {
- uprv_memcpy(j,smallBuffer,sizeof(j[0])*jSize);
- }
-#if defined(ULOC_DEBUG)
- fprintf(stderr,"malloced at size %d\n", jSize);
-#endif
- } else {
- j = static_cast<_acceptLangItem *>(uprv_realloc(j, sizeof(j[0])*jSize*2));
+ if(n>=items.getCapacity()) { // If we need more items
+ if(NULL == items.resize(items.getCapacity()*2, items.getCapacity())) {
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ return -1;
+ }
#if defined(ULOC_DEBUG)
- fprintf(stderr,"re-alloced at size %d\n", jSize);
+ fprintf(stderr,"malloced at size %d\n", items.getCapacity());
#endif
- }
- jSize *= 2;
- if(j==NULL) {
- *status = U_MEMORY_ALLOCATION_ERROR;
- return -1;
- }
}
}
- uprv_sortArray(j, n, sizeof(j[0]), uloc_acceptLanguageCompare, NULL, TRUE, status);
- if(U_FAILURE(*status)) {
- if(j != smallBuffer) {
-#if defined(ULOC_DEBUG)
- fprintf(stderr,"freeing j %p\n", j);
-#endif
- uprv_free(j);
- }
+ uprv_sortArray(items.getAlias(), n, sizeof(items[0]), uloc_acceptLanguageCompare, NULL, TRUE, status);
+ if (U_FAILURE(*status)) {
return -1;
}
- strs = static_cast<char **>(uprv_malloc((size_t)(sizeof(strs[0])*n)));
- /* Check for null pointer */
- if (strs == NULL) {
- uprv_free(j); /* Free to avoid memory leak */
+ LocalMemory<const char*> strs(NULL);
+ if (strs.allocateInsteadAndReset(n) == NULL) {
*status = U_MEMORY_ALLOCATION_ERROR;
return -1;
}
#if defined(ULOC_DEBUG)
/*fprintf(stderr,"%d: s <%s> q <%g>\n", i, j[i].locale, j[i].q);*/
#endif
- strs[i]=j[i].locale;
- }
- res = uloc_acceptLanguage(result, resultAvailable, outResult,
- (const char**)strs, n, availableLocales, status);
- for(i=0;i<n;i++) {
- uprv_free(strs[i]);
- }
- uprv_free(strs);
- if(j != smallBuffer) {
-#if defined(ULOC_DEBUG)
- fprintf(stderr,"freeing j %p\n", j);
-#endif
- uprv_free(j);
+ strs[i]=items[i].locale;
}
+ res = uloc_acceptLanguage(result, resultAvailable, outResult,
+ strs.getAlias(), n, availableLocales, status);
return res;
}
U_CAPI int32_t U_EXPORT2
-uloc_acceptLanguage(char *result, int32_t resultAvailable,
+uloc_acceptLanguage(char *result, int32_t resultAvailable,
UAcceptResult *outResult, const char **acceptList,
int32_t acceptListCount,
UEnumeration* availableLocales,
#if defined(ULOC_DEBUG)
fprintf(stderr,"%02d: %s\n", i, acceptList[i]);
#endif
- while((l=uenum_next(availableLocales, NULL, status))) {
+ while((l=uenum_next(availableLocales, NULL, status)) != NULL) {
#if defined(ULOC_DEBUG)
fprintf(stderr," %s\n", l);
#endif
len = (int32_t)uprv_strlen(l);
if(!uprv_strcmp(acceptList[i], l)) {
- if(outResult) {
+ if(outResult) {
*outResult = ULOC_ACCEPT_VALID;
}
#if defined(ULOC_DEBUG)
uprv_free(fallbackList[j]);
}
uprv_free(fallbackList);
- return u_terminateChars(result, resultAvailable, len, status);
+ return u_terminateChars(result, resultAvailable, len, status);
}
if(len>maxLen) {
maxLen = len;
}
}
- uenum_reset(availableLocales, status);
+ uenum_reset(availableLocales, status);
/* save off parent info */
- if(uloc_getParent(acceptList[i], tmp, sizeof(tmp)/sizeof(tmp[0]), status)!=0) {
+ if(uloc_getParent(acceptList[i], tmp, UPRV_LENGTHOF(tmp), status)!=0) {
fallbackList[i] = uprv_strdup(tmp);
} else {
fallbackList[i]=0;
#if defined(ULOC_DEBUG)
fprintf(stderr,"Try: [%s]", fallbackList[i]);
#endif
- while((l=uenum_next(availableLocales, NULL, status))) {
+ while((l=uenum_next(availableLocales, NULL, status)) != NULL) {
#if defined(ULOC_DEBUG)
fprintf(stderr," %s\n", l);
#endif
len = (int32_t)uprv_strlen(l);
if(!uprv_strcmp(fallbackList[i], l)) {
- if(outResult) {
+ if(outResult) {
*outResult = ULOC_ACCEPT_FALLBACK;
}
#if defined(ULOC_DEBUG)
return u_terminateChars(result, resultAvailable, len, status);
}
}
- uenum_reset(availableLocales, status);
+ uenum_reset(availableLocales, status);
- if(uloc_getParent(fallbackList[i], tmp, sizeof(tmp)/sizeof(tmp[0]), status)!=0) {
+ if(uloc_getParent(fallbackList[i], tmp, UPRV_LENGTHOF(tmp), status)!=0) {
uprv_free(fallbackList[i]);
fallbackList[i] = uprv_strdup(tmp);
} else {
}
}
}
- if(outResult) {
+ if(outResult) {
*outResult = ULOC_ACCEPT_FAILED;
}
}
return bcpType;
}
-#define UPRV_ISDIGIT(c) (((c) >= '0') && ((c) <= '9'))
-#define UPRV_ISALPHANUM(c) (uprv_isASCIILetter(c) || UPRV_ISDIGIT(c) )
-
static UBool
isWellFormedLegacyKey(const char* legacyKey)
{
// Checks if the specified locale key is well-formed with the legacy locale syntax.
//
// Note:
- // Neither ICU nor LDML/CLDR provides the definition of keyword syntax.
- // However, a key should not contain '=' obviously. For now, all existing
- // keys are using ASCII alphabetic letters only. We won't add any new key
- // that is not compatible with the BCP 47 syntax. Therefore, we assume
- // a valid key consist from [0-9a-zA-Z], no symbols.
+ // LDML/CLDR provides some definition of keyword syntax in
+ // * http://www.unicode.org/reports/tr35/#Unicode_locale_identifier and
+ // * http://www.unicode.org/reports/tr35/#Old_Locale_Extension_Syntax
+ // Keys can only consist of [0-9a-zA-Z].
if (isWellFormedLegacyKey(keyword)) {
return keyword;
}
// Checks if the specified locale type is well-formed with the legacy locale syntax.
//
// Note:
- // Neither ICU nor LDML/CLDR provides the definition of keyword syntax.
- // However, a type should not contain '=' obviously. For now, all existing
- // types are using ASCII alphabetic letters with a few symbol letters. We won't
- // add any new type that is not compatible with the BCP 47 syntax except timezone
- // IDs. For now, we assume a valid type start with [0-9a-zA-Z], but may contain
- // '-' '_' '/' in the middle.
+ // LDML/CLDR provides some definition of keyword syntax in
+ // * http://www.unicode.org/reports/tr35/#Unicode_locale_identifier and
+ // * http://www.unicode.org/reports/tr35/#Old_Locale_Extension_Syntax
+ // Values (types) can only consist of [0-9a-zA-Z], plus for legacy values
+ // we allow [/_-+] in the middle (e.g. "Etc/GMT+1", "Asia/Tel_Aviv")
if (isWellFormedLegacyType(value)) {
return value;
}