Line data Source code
1 : // © 2016 and later: Unicode, Inc. and others.
2 : // License & terms of use: http://www.unicode.org/copyright.html
3 : /*
4 : **********************************************************************
5 : * Copyright (C) 2009-2015, International Business Machines
6 : * Corporation and others. All Rights Reserved.
7 : **********************************************************************
8 : */
9 :
10 : #include "unicode/utypes.h"
11 : #include "unicode/ures.h"
12 : #include "unicode/putil.h"
13 : #include "unicode/uloc.h"
14 : #include "ustr_imp.h"
15 : #include "cmemory.h"
16 : #include "cstring.h"
17 : #include "putilimp.h"
18 : #include "uinvchar.h"
19 : #include "ulocimp.h"
20 : #include "uassert.h"
21 :
22 :
23 : /* struct holding a single variant */
24 : typedef struct VariantListEntry {
25 : const char *variant;
26 : struct VariantListEntry *next;
27 : } VariantListEntry;
28 :
29 : /* struct holding a single attribute value */
30 : typedef struct AttributeListEntry {
31 : const char *attribute;
32 : struct AttributeListEntry *next;
33 : } AttributeListEntry;
34 :
35 : /* struct holding a single extension */
36 : typedef struct ExtensionListEntry {
37 : const char *key;
38 : const char *value;
39 : struct ExtensionListEntry *next;
40 : } ExtensionListEntry;
41 :
42 : #define MAXEXTLANG 3
43 : typedef struct ULanguageTag {
44 : char *buf; /* holding parsed subtags */
45 : const char *language;
46 : const char *extlang[MAXEXTLANG];
47 : const char *script;
48 : const char *region;
49 : VariantListEntry *variants;
50 : ExtensionListEntry *extensions;
51 : const char *privateuse;
52 : const char *grandfathered;
53 : } ULanguageTag;
54 :
55 : #define MINLEN 2
56 : #define SEP '-'
57 : #define PRIVATEUSE 'x'
58 : #define LDMLEXT 'u'
59 :
60 : #define LOCALE_SEP '_'
61 : #define LOCALE_EXT_SEP '@'
62 : #define LOCALE_KEYWORD_SEP ';'
63 : #define LOCALE_KEY_TYPE_SEP '='
64 :
65 : #define ISALPHA(c) uprv_isASCIILetter(c)
66 : #define ISNUMERIC(c) ((c)>='0' && (c)<='9')
67 :
68 : static const char EMPTY[] = "";
69 : static const char LANG_UND[] = "und";
70 : static const char PRIVATEUSE_KEY[] = "x";
71 : static const char _POSIX[] = "_POSIX";
72 : static const char POSIX_KEY[] = "va";
73 : static const char POSIX_VALUE[] = "posix";
74 : static const char LOCALE_ATTRIBUTE_KEY[] = "attribute";
75 : static const char PRIVUSE_VARIANT_PREFIX[] = "lvariant";
76 : static const char LOCALE_TYPE_YES[] = "yes";
77 :
78 : #define LANG_UND_LEN 3
79 :
80 : static const char* const GRANDFATHERED[] = {
81 : /* grandfathered preferred */
82 : "art-lojban", "jbo",
83 : "cel-gaulish", "xtg-x-cel-gaulish",
84 : "en-GB-oed", "en-GB-x-oed",
85 : "i-ami", "ami",
86 : "i-bnn", "bnn",
87 : "i-default", "en-x-i-default",
88 : "i-enochian", "und-x-i-enochian",
89 : "i-hak", "hak",
90 : "i-klingon", "tlh",
91 : "i-lux", "lb",
92 : "i-mingo", "see-x-i-mingo",
93 : "i-navajo", "nv",
94 : "i-pwn", "pwn",
95 : "i-tao", "tao",
96 : "i-tay", "tay",
97 : "i-tsu", "tsu",
98 : "no-bok", "nb",
99 : "no-nyn", "nn",
100 : "sgn-be-fr", "sfb",
101 : "sgn-be-nl", "vgt",
102 : "sgn-ch-de", "sgg",
103 : "zh-guoyu", "cmn",
104 : "zh-hakka", "hak",
105 : "zh-min", "nan-x-zh-min",
106 : "zh-min-nan", "nan",
107 : "zh-xiang", "hsn",
108 : NULL, NULL
109 : };
110 :
111 : static const char DEPRECATEDLANGS[][4] = {
112 : /* deprecated new */
113 : "iw", "he",
114 : "ji", "yi",
115 : "in", "id"
116 : };
117 :
118 : /*
119 : * -------------------------------------------------
120 : *
121 : * These ultag_ functions may be exposed as APIs later
122 : *
123 : * -------------------------------------------------
124 : */
125 :
126 : static ULanguageTag*
127 : ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* status);
128 :
129 : static void
130 : ultag_close(ULanguageTag* langtag);
131 :
132 : static const char*
133 : ultag_getLanguage(const ULanguageTag* langtag);
134 :
135 : #if 0
136 : static const char*
137 : ultag_getJDKLanguage(const ULanguageTag* langtag);
138 : #endif
139 :
140 : static const char*
141 : ultag_getExtlang(const ULanguageTag* langtag, int32_t idx);
142 :
143 : static int32_t
144 : ultag_getExtlangSize(const ULanguageTag* langtag);
145 :
146 : static const char*
147 : ultag_getScript(const ULanguageTag* langtag);
148 :
149 : static const char*
150 : ultag_getRegion(const ULanguageTag* langtag);
151 :
152 : static const char*
153 : ultag_getVariant(const ULanguageTag* langtag, int32_t idx);
154 :
155 : static int32_t
156 : ultag_getVariantsSize(const ULanguageTag* langtag);
157 :
158 : static const char*
159 : ultag_getExtensionKey(const ULanguageTag* langtag, int32_t idx);
160 :
161 : static const char*
162 : ultag_getExtensionValue(const ULanguageTag* langtag, int32_t idx);
163 :
164 : static int32_t
165 : ultag_getExtensionsSize(const ULanguageTag* langtag);
166 :
167 : static const char*
168 : ultag_getPrivateUse(const ULanguageTag* langtag);
169 :
170 : #if 0
171 : static const char*
172 : ultag_getGrandfathered(const ULanguageTag* langtag);
173 : #endif
174 :
175 : /*
176 : * -------------------------------------------------
177 : *
178 : * Language subtag syntax validation functions
179 : *
180 : * -------------------------------------------------
181 : */
182 :
183 : static UBool
184 22 : _isAlphaString(const char* s, int32_t len) {
185 : int32_t i;
186 66 : for (i = 0; i < len; i++) {
187 44 : if (!ISALPHA(*(s + i))) {
188 0 : return FALSE;
189 : }
190 : }
191 22 : return TRUE;
192 : }
193 :
194 : static UBool
195 0 : _isNumericString(const char* s, int32_t len) {
196 : int32_t i;
197 0 : for (i = 0; i < len; i++) {
198 0 : if (!ISNUMERIC(*(s + i))) {
199 0 : return FALSE;
200 : }
201 : }
202 0 : return TRUE;
203 : }
204 :
205 : static UBool
206 0 : _isAlphaNumericString(const char* s, int32_t len) {
207 : int32_t i;
208 0 : for (i = 0; i < len; i++) {
209 0 : if (!ISALPHA(*(s + i)) && !ISNUMERIC(*(s + i))) {
210 0 : return FALSE;
211 : }
212 : }
213 0 : return TRUE;
214 : }
215 :
216 : static UBool
217 11 : _isLanguageSubtag(const char* s, int32_t len) {
218 : /*
219 : * language = 2*3ALPHA ; shortest ISO 639 code
220 : * ["-" extlang] ; sometimes followed by
221 : * ; extended language subtags
222 : * / 4ALPHA ; or reserved for future use
223 : * / 5*8ALPHA ; or registered language subtag
224 : */
225 11 : if (len < 0) {
226 0 : len = (int32_t)uprv_strlen(s);
227 : }
228 11 : if (len >= 2 && len <= 8 && _isAlphaString(s, len)) {
229 11 : return TRUE;
230 : }
231 0 : return FALSE;
232 : }
233 :
234 : static UBool
235 0 : _isExtlangSubtag(const char* s, int32_t len) {
236 : /*
237 : * extlang = 3ALPHA ; selected ISO 639 codes
238 : * *2("-" 3ALPHA) ; permanently reserved
239 : */
240 0 : if (len < 0) {
241 0 : len = (int32_t)uprv_strlen(s);
242 : }
243 0 : if (len == 3 && _isAlphaString(s, len)) {
244 0 : return TRUE;
245 : }
246 0 : return FALSE;
247 : }
248 :
249 : static UBool
250 0 : _isScriptSubtag(const char* s, int32_t len) {
251 : /*
252 : * script = 4ALPHA ; ISO 15924 code
253 : */
254 0 : if (len < 0) {
255 0 : len = (int32_t)uprv_strlen(s);
256 : }
257 0 : if (len == 4 && _isAlphaString(s, len)) {
258 0 : return TRUE;
259 : }
260 0 : return FALSE;
261 : }
262 :
263 : static UBool
264 11 : _isRegionSubtag(const char* s, int32_t len) {
265 : /*
266 : * region = 2ALPHA ; ISO 3166-1 code
267 : * / 3DIGIT ; UN M.49 code
268 : */
269 11 : if (len < 0) {
270 0 : len = (int32_t)uprv_strlen(s);
271 : }
272 11 : if (len == 2 && _isAlphaString(s, len)) {
273 11 : return TRUE;
274 : }
275 0 : if (len == 3 && _isNumericString(s, len)) {
276 0 : return TRUE;
277 : }
278 0 : return FALSE;
279 : }
280 :
281 : static UBool
282 0 : _isVariantSubtag(const char* s, int32_t len) {
283 : /*
284 : * variant = 5*8alphanum ; registered variants
285 : * / (DIGIT 3alphanum)
286 : */
287 0 : if (len < 0) {
288 0 : len = (int32_t)uprv_strlen(s);
289 : }
290 0 : if (len >= 5 && len <= 8 && _isAlphaNumericString(s, len)) {
291 0 : return TRUE;
292 : }
293 0 : if (len == 4 && ISNUMERIC(*s) && _isAlphaNumericString(s + 1, 3)) {
294 0 : return TRUE;
295 : }
296 0 : return FALSE;
297 : }
298 :
299 : static UBool
300 0 : _isPrivateuseVariantSubtag(const char* s, int32_t len) {
301 : /*
302 : * variant = 1*8alphanum ; registered variants
303 : * / (DIGIT 3alphanum)
304 : */
305 0 : if (len < 0) {
306 0 : len = (int32_t)uprv_strlen(s);
307 : }
308 0 : if (len >= 1 && len <= 8 && _isAlphaNumericString(s, len)) {
309 0 : return TRUE;
310 : }
311 0 : return FALSE;
312 : }
313 :
314 : static UBool
315 0 : _isExtensionSingleton(const char* s, int32_t len) {
316 : /*
317 : * extension = singleton 1*("-" (2*8alphanum))
318 : */
319 0 : if (len < 0) {
320 0 : len = (int32_t)uprv_strlen(s);
321 : }
322 0 : if (len == 1 && ISALPHA(*s) && (uprv_tolower(*s) != PRIVATEUSE)) {
323 0 : return TRUE;
324 : }
325 0 : return FALSE;
326 : }
327 :
328 : static UBool
329 0 : _isExtensionSubtag(const char* s, int32_t len) {
330 : /*
331 : * extension = singleton 1*("-" (2*8alphanum))
332 : */
333 0 : if (len < 0) {
334 0 : len = (int32_t)uprv_strlen(s);
335 : }
336 0 : if (len >= 2 && len <= 8 && _isAlphaNumericString(s, len)) {
337 0 : return TRUE;
338 : }
339 0 : return FALSE;
340 : }
341 :
342 : static UBool
343 0 : _isExtensionSubtags(const char* s, int32_t len) {
344 0 : const char *p = s;
345 0 : const char *pSubtag = NULL;
346 :
347 0 : if (len < 0) {
348 0 : len = (int32_t)uprv_strlen(s);
349 : }
350 :
351 0 : while ((p - s) < len) {
352 0 : if (*p == SEP) {
353 0 : if (pSubtag == NULL) {
354 0 : return FALSE;
355 : }
356 0 : if (!_isExtensionSubtag(pSubtag, (int32_t)(p - pSubtag))) {
357 0 : return FALSE;
358 : }
359 0 : pSubtag = NULL;
360 0 : } else if (pSubtag == NULL) {
361 0 : pSubtag = p;
362 : }
363 0 : p++;
364 : }
365 0 : if (pSubtag == NULL) {
366 0 : return FALSE;
367 : }
368 0 : return _isExtensionSubtag(pSubtag, (int32_t)(p - pSubtag));
369 : }
370 :
371 : static UBool
372 0 : _isPrivateuseValueSubtag(const char* s, int32_t len) {
373 : /*
374 : * privateuse = "x" 1*("-" (1*8alphanum))
375 : */
376 0 : if (len < 0) {
377 0 : len = (int32_t)uprv_strlen(s);
378 : }
379 0 : if (len >= 1 && len <= 8 && _isAlphaNumericString(s, len)) {
380 0 : return TRUE;
381 : }
382 0 : return FALSE;
383 : }
384 :
385 : static UBool
386 0 : _isPrivateuseValueSubtags(const char* s, int32_t len) {
387 0 : const char *p = s;
388 0 : const char *pSubtag = NULL;
389 :
390 0 : if (len < 0) {
391 0 : len = (int32_t)uprv_strlen(s);
392 : }
393 :
394 0 : while ((p - s) < len) {
395 0 : if (*p == SEP) {
396 0 : if (pSubtag == NULL) {
397 0 : return FALSE;
398 : }
399 0 : if (!_isPrivateuseValueSubtag(pSubtag, (int32_t)(p - pSubtag))) {
400 0 : return FALSE;
401 : }
402 0 : pSubtag = NULL;
403 0 : } else if (pSubtag == NULL) {
404 0 : pSubtag = p;
405 : }
406 0 : p++;
407 : }
408 0 : if (pSubtag == NULL) {
409 0 : return FALSE;
410 : }
411 0 : return _isPrivateuseValueSubtag(pSubtag, (int32_t)(p - pSubtag));
412 : }
413 :
414 : U_CFUNC UBool
415 0 : ultag_isUnicodeLocaleKey(const char* s, int32_t len) {
416 0 : if (len < 0) {
417 0 : len = (int32_t)uprv_strlen(s);
418 : }
419 0 : if (len == 2 && _isAlphaNumericString(s, len)) {
420 0 : return TRUE;
421 : }
422 0 : return FALSE;
423 : }
424 :
425 : U_CFUNC UBool
426 0 : ultag_isUnicodeLocaleType(const char*s, int32_t len) {
427 : const char* p;
428 0 : int32_t subtagLen = 0;
429 :
430 0 : if (len < 0) {
431 0 : len = (int32_t)uprv_strlen(s);
432 : }
433 :
434 0 : for (p = s; len > 0; p++, len--) {
435 0 : if (*p == SEP) {
436 0 : if (subtagLen < 3) {
437 0 : return FALSE;
438 : }
439 0 : subtagLen = 0;
440 0 : } else if (ISALPHA(*p) || ISNUMERIC(*p)) {
441 0 : subtagLen++;
442 0 : if (subtagLen > 8) {
443 0 : return FALSE;
444 : }
445 : } else {
446 0 : return FALSE;
447 : }
448 : }
449 :
450 0 : return (subtagLen >= 3);
451 : }
452 : /*
453 : * -------------------------------------------------
454 : *
455 : * Helper functions
456 : *
457 : * -------------------------------------------------
458 : */
459 :
460 : static UBool
461 0 : _addVariantToList(VariantListEntry **first, VariantListEntry *var) {
462 0 : UBool bAdded = TRUE;
463 :
464 0 : if (*first == NULL) {
465 0 : var->next = NULL;
466 0 : *first = var;
467 : } else {
468 : VariantListEntry *prev, *cur;
469 : int32_t cmp;
470 :
471 : /* variants order should be preserved */
472 0 : prev = NULL;
473 0 : cur = *first;
474 : while (TRUE) {
475 0 : if (cur == NULL) {
476 0 : prev->next = var;
477 0 : var->next = NULL;
478 0 : break;
479 : }
480 :
481 : /* Checking for duplicate variant */
482 0 : cmp = uprv_compareInvCharsAsAscii(var->variant, cur->variant);
483 0 : if (cmp == 0) {
484 : /* duplicated variant */
485 0 : bAdded = FALSE;
486 0 : break;
487 : }
488 0 : prev = cur;
489 0 : cur = cur->next;
490 : }
491 : }
492 :
493 0 : return bAdded;
494 : }
495 :
496 : static UBool
497 0 : _addAttributeToList(AttributeListEntry **first, AttributeListEntry *attr) {
498 0 : UBool bAdded = TRUE;
499 :
500 0 : if (*first == NULL) {
501 0 : attr->next = NULL;
502 0 : *first = attr;
503 : } else {
504 : AttributeListEntry *prev, *cur;
505 : int32_t cmp;
506 :
507 : /* reorder variants in alphabetical order */
508 0 : prev = NULL;
509 0 : cur = *first;
510 : while (TRUE) {
511 0 : if (cur == NULL) {
512 0 : prev->next = attr;
513 0 : attr->next = NULL;
514 0 : break;
515 : }
516 0 : cmp = uprv_compareInvCharsAsAscii(attr->attribute, cur->attribute);
517 0 : if (cmp < 0) {
518 0 : if (prev == NULL) {
519 0 : *first = attr;
520 : } else {
521 0 : prev->next = attr;
522 : }
523 0 : attr->next = cur;
524 0 : break;
525 : }
526 0 : if (cmp == 0) {
527 : /* duplicated variant */
528 0 : bAdded = FALSE;
529 0 : break;
530 : }
531 0 : prev = cur;
532 0 : cur = cur->next;
533 : }
534 : }
535 :
536 0 : return bAdded;
537 : }
538 :
539 :
540 : static UBool
541 0 : _addExtensionToList(ExtensionListEntry **first, ExtensionListEntry *ext, UBool localeToBCP) {
542 0 : UBool bAdded = TRUE;
543 :
544 0 : if (*first == NULL) {
545 0 : ext->next = NULL;
546 0 : *first = ext;
547 : } else {
548 : ExtensionListEntry *prev, *cur;
549 : int32_t cmp;
550 :
551 : /* reorder variants in alphabetical order */
552 0 : prev = NULL;
553 0 : cur = *first;
554 : while (TRUE) {
555 0 : if (cur == NULL) {
556 0 : prev->next = ext;
557 0 : ext->next = NULL;
558 0 : break;
559 : }
560 0 : if (localeToBCP) {
561 : /* special handling for locale to bcp conversion */
562 : int32_t len, curlen;
563 :
564 0 : len = (int32_t)uprv_strlen(ext->key);
565 0 : curlen = (int32_t)uprv_strlen(cur->key);
566 :
567 0 : if (len == 1 && curlen == 1) {
568 0 : if (*(ext->key) == *(cur->key)) {
569 0 : cmp = 0;
570 0 : } else if (*(ext->key) == PRIVATEUSE) {
571 0 : cmp = 1;
572 0 : } else if (*(cur->key) == PRIVATEUSE) {
573 0 : cmp = -1;
574 : } else {
575 0 : cmp = *(ext->key) - *(cur->key);
576 : }
577 0 : } else if (len == 1) {
578 0 : cmp = *(ext->key) - LDMLEXT;
579 0 : } else if (curlen == 1) {
580 0 : cmp = LDMLEXT - *(cur->key);
581 : } else {
582 0 : cmp = uprv_compareInvCharsAsAscii(ext->key, cur->key);
583 : /* Both are u extension keys - we need special handling for 'attribute' */
584 0 : if (cmp != 0) {
585 0 : if (uprv_strcmp(cur->key, LOCALE_ATTRIBUTE_KEY) == 0) {
586 0 : cmp = 1;
587 0 : } else if (uprv_strcmp(ext->key, LOCALE_ATTRIBUTE_KEY) == 0) {
588 0 : cmp = -1;
589 : }
590 : }
591 : }
592 : } else {
593 0 : cmp = uprv_compareInvCharsAsAscii(ext->key, cur->key);
594 : }
595 0 : if (cmp < 0) {
596 0 : if (prev == NULL) {
597 0 : *first = ext;
598 : } else {
599 0 : prev->next = ext;
600 : }
601 0 : ext->next = cur;
602 0 : break;
603 : }
604 0 : if (cmp == 0) {
605 : /* duplicated extension key */
606 0 : bAdded = FALSE;
607 0 : break;
608 : }
609 0 : prev = cur;
610 0 : cur = cur->next;
611 0 : }
612 : }
613 :
614 0 : return bAdded;
615 : }
616 :
617 : static void
618 0 : _initializeULanguageTag(ULanguageTag* langtag) {
619 : int32_t i;
620 :
621 0 : langtag->buf = NULL;
622 :
623 0 : langtag->language = EMPTY;
624 0 : for (i = 0; i < MAXEXTLANG; i++) {
625 0 : langtag->extlang[i] = NULL;
626 : }
627 :
628 0 : langtag->script = EMPTY;
629 0 : langtag->region = EMPTY;
630 :
631 0 : langtag->variants = NULL;
632 0 : langtag->extensions = NULL;
633 :
634 0 : langtag->grandfathered = EMPTY;
635 0 : langtag->privateuse = EMPTY;
636 0 : }
637 :
638 : static int32_t
639 11 : _appendLanguageToLanguageTag(const char* localeID, char* appendAt, int32_t capacity, UBool strict, UErrorCode* status) {
640 : char buf[ULOC_LANG_CAPACITY];
641 11 : UErrorCode tmpStatus = U_ZERO_ERROR;
642 : int32_t len, i;
643 11 : int32_t reslen = 0;
644 :
645 11 : if (U_FAILURE(*status)) {
646 0 : return 0;
647 : }
648 :
649 11 : len = uloc_getLanguage(localeID, buf, sizeof(buf), &tmpStatus);
650 11 : if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) {
651 0 : if (strict) {
652 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
653 0 : return 0;
654 : }
655 0 : len = 0;
656 : }
657 :
658 : /* Note: returned language code is in lower case letters */
659 :
660 11 : if (len == 0) {
661 0 : if (reslen < capacity) {
662 0 : uprv_memcpy(appendAt + reslen, LANG_UND, uprv_min(LANG_UND_LEN, capacity - reslen));
663 : }
664 0 : reslen += LANG_UND_LEN;
665 11 : } else if (!_isLanguageSubtag(buf, len)) {
666 : /* invalid language code */
667 0 : if (strict) {
668 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
669 0 : return 0;
670 : }
671 0 : if (reslen < capacity) {
672 0 : uprv_memcpy(appendAt + reslen, LANG_UND, uprv_min(LANG_UND_LEN, capacity - reslen));
673 : }
674 0 : reslen += LANG_UND_LEN;
675 : } else {
676 : /* resolve deprecated */
677 44 : for (i = 0; i < UPRV_LENGTHOF(DEPRECATEDLANGS); i += 2) {
678 33 : if (uprv_compareInvCharsAsAscii(buf, DEPRECATEDLANGS[i]) == 0) {
679 0 : uprv_strcpy(buf, DEPRECATEDLANGS[i + 1]);
680 0 : len = (int32_t)uprv_strlen(buf);
681 0 : break;
682 : }
683 : }
684 11 : if (reslen < capacity) {
685 11 : uprv_memcpy(appendAt + reslen, buf, uprv_min(len, capacity - reslen));
686 : }
687 11 : reslen += len;
688 : }
689 11 : u_terminateChars(appendAt, capacity, reslen, status);
690 11 : return reslen;
691 : }
692 :
693 : static int32_t
694 11 : _appendScriptToLanguageTag(const char* localeID, char* appendAt, int32_t capacity, UBool strict, UErrorCode* status) {
695 : char buf[ULOC_SCRIPT_CAPACITY];
696 11 : UErrorCode tmpStatus = U_ZERO_ERROR;
697 : int32_t len;
698 11 : int32_t reslen = 0;
699 :
700 11 : if (U_FAILURE(*status)) {
701 0 : return 0;
702 : }
703 :
704 11 : len = uloc_getScript(localeID, buf, sizeof(buf), &tmpStatus);
705 11 : if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) {
706 0 : if (strict) {
707 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
708 : }
709 0 : return 0;
710 : }
711 :
712 11 : if (len > 0) {
713 0 : if (!_isScriptSubtag(buf, len)) {
714 : /* invalid script code */
715 0 : if (strict) {
716 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
717 : }
718 0 : return 0;
719 : } else {
720 0 : if (reslen < capacity) {
721 0 : *(appendAt + reslen) = SEP;
722 : }
723 0 : reslen++;
724 :
725 0 : if (reslen < capacity) {
726 0 : uprv_memcpy(appendAt + reslen, buf, uprv_min(len, capacity - reslen));
727 : }
728 0 : reslen += len;
729 : }
730 : }
731 11 : u_terminateChars(appendAt, capacity, reslen, status);
732 11 : return reslen;
733 : }
734 :
735 : static int32_t
736 11 : _appendRegionToLanguageTag(const char* localeID, char* appendAt, int32_t capacity, UBool strict, UErrorCode* status) {
737 : char buf[ULOC_COUNTRY_CAPACITY];
738 11 : UErrorCode tmpStatus = U_ZERO_ERROR;
739 : int32_t len;
740 11 : int32_t reslen = 0;
741 :
742 11 : if (U_FAILURE(*status)) {
743 0 : return 0;
744 : }
745 :
746 11 : len = uloc_getCountry(localeID, buf, sizeof(buf), &tmpStatus);
747 11 : if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) {
748 0 : if (strict) {
749 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
750 : }
751 0 : return 0;
752 : }
753 :
754 11 : if (len > 0) {
755 11 : if (!_isRegionSubtag(buf, len)) {
756 : /* invalid region code */
757 0 : if (strict) {
758 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
759 : }
760 0 : return 0;
761 : } else {
762 11 : if (reslen < capacity) {
763 11 : *(appendAt + reslen) = SEP;
764 : }
765 11 : reslen++;
766 :
767 11 : if (reslen < capacity) {
768 11 : uprv_memcpy(appendAt + reslen, buf, uprv_min(len, capacity - reslen));
769 : }
770 11 : reslen += len;
771 : }
772 : }
773 11 : u_terminateChars(appendAt, capacity, reslen, status);
774 11 : return reslen;
775 : }
776 :
777 : static int32_t
778 11 : _appendVariantsToLanguageTag(const char* localeID, char* appendAt, int32_t capacity, UBool strict, UBool *hadPosix, UErrorCode* status) {
779 : char buf[ULOC_FULLNAME_CAPACITY];
780 11 : UErrorCode tmpStatus = U_ZERO_ERROR;
781 : int32_t len, i;
782 11 : int32_t reslen = 0;
783 :
784 11 : if (U_FAILURE(*status)) {
785 0 : return 0;
786 : }
787 :
788 11 : len = uloc_getVariant(localeID, buf, sizeof(buf), &tmpStatus);
789 11 : if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) {
790 0 : if (strict) {
791 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
792 : }
793 0 : return 0;
794 : }
795 :
796 11 : if (len > 0) {
797 : char *p, *pVar;
798 0 : UBool bNext = TRUE;
799 : VariantListEntry *var;
800 0 : VariantListEntry *varFirst = NULL;
801 :
802 0 : pVar = NULL;
803 0 : p = buf;
804 0 : while (bNext) {
805 0 : if (*p == SEP || *p == LOCALE_SEP || *p == 0) {
806 0 : if (*p == 0) {
807 0 : bNext = FALSE;
808 : } else {
809 0 : *p = 0; /* terminate */
810 : }
811 0 : if (pVar == NULL) {
812 0 : if (strict) {
813 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
814 0 : break;
815 : }
816 : /* ignore empty variant */
817 : } else {
818 : /* ICU uses upper case letters for variants, but
819 : the canonical format is lowercase in BCP47 */
820 0 : for (i = 0; *(pVar + i) != 0; i++) {
821 0 : *(pVar + i) = uprv_tolower(*(pVar + i));
822 : }
823 :
824 : /* validate */
825 0 : if (_isVariantSubtag(pVar, -1)) {
826 0 : if (uprv_strcmp(pVar,POSIX_VALUE) || len != (int32_t)uprv_strlen(POSIX_VALUE)) {
827 : /* emit the variant to the list */
828 0 : var = (VariantListEntry*)uprv_malloc(sizeof(VariantListEntry));
829 0 : if (var == NULL) {
830 0 : *status = U_MEMORY_ALLOCATION_ERROR;
831 0 : break;
832 : }
833 0 : var->variant = pVar;
834 0 : if (!_addVariantToList(&varFirst, var)) {
835 : /* duplicated variant */
836 0 : uprv_free(var);
837 0 : if (strict) {
838 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
839 0 : break;
840 : }
841 : }
842 : } else {
843 : /* Special handling for POSIX variant, need to remember that we had it and then */
844 : /* treat it like an extension later. */
845 0 : *hadPosix = TRUE;
846 : }
847 0 : } else if (strict) {
848 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
849 0 : break;
850 0 : } else if (_isPrivateuseValueSubtag(pVar, -1)) {
851 : /* Handle private use subtags separately */
852 0 : break;
853 : }
854 : }
855 : /* reset variant starting position */
856 0 : pVar = NULL;
857 0 : } else if (pVar == NULL) {
858 0 : pVar = p;
859 : }
860 0 : p++;
861 : }
862 :
863 0 : if (U_SUCCESS(*status)) {
864 0 : if (varFirst != NULL) {
865 : int32_t varLen;
866 :
867 : /* write out validated/normalized variants to the target */
868 0 : var = varFirst;
869 0 : while (var != NULL) {
870 0 : if (reslen < capacity) {
871 0 : *(appendAt + reslen) = SEP;
872 : }
873 0 : reslen++;
874 0 : varLen = (int32_t)uprv_strlen(var->variant);
875 0 : if (reslen < capacity) {
876 0 : uprv_memcpy(appendAt + reslen, var->variant, uprv_min(varLen, capacity - reslen));
877 : }
878 0 : reslen += varLen;
879 0 : var = var->next;
880 : }
881 : }
882 : }
883 :
884 : /* clean up */
885 0 : var = varFirst;
886 0 : while (var != NULL) {
887 0 : VariantListEntry *tmpVar = var->next;
888 0 : uprv_free(var);
889 0 : var = tmpVar;
890 : }
891 :
892 0 : if (U_FAILURE(*status)) {
893 0 : return 0;
894 : }
895 : }
896 :
897 11 : u_terminateChars(appendAt, capacity, reslen, status);
898 11 : return reslen;
899 : }
900 :
901 : static int32_t
902 11 : _appendKeywordsToLanguageTag(const char* localeID, char* appendAt, int32_t capacity, UBool strict, UBool hadPosix, UErrorCode* status) {
903 : char buf[ULOC_KEYWORD_AND_VALUES_CAPACITY];
904 11 : char attrBuf[ULOC_KEYWORD_AND_VALUES_CAPACITY] = { 0 };
905 11 : int32_t attrBufLength = 0;
906 11 : UEnumeration *keywordEnum = NULL;
907 11 : int32_t reslen = 0;
908 :
909 11 : keywordEnum = uloc_openKeywords(localeID, status);
910 11 : if (U_FAILURE(*status) && !hadPosix) {
911 0 : uenum_close(keywordEnum);
912 0 : return 0;
913 : }
914 11 : if (keywordEnum != NULL || hadPosix) {
915 : /* reorder extensions */
916 : int32_t len;
917 : const char *key;
918 0 : ExtensionListEntry *firstExt = NULL;
919 : ExtensionListEntry *ext;
920 0 : AttributeListEntry *firstAttr = NULL;
921 : AttributeListEntry *attr;
922 : char *attrValue;
923 : char extBuf[ULOC_KEYWORD_AND_VALUES_CAPACITY];
924 0 : char *pExtBuf = extBuf;
925 0 : int32_t extBufCapacity = sizeof(extBuf);
926 0 : const char *bcpKey=nullptr, *bcpValue=nullptr;
927 0 : UErrorCode tmpStatus = U_ZERO_ERROR;
928 : int32_t keylen;
929 : UBool isBcpUExt;
930 :
931 : while (TRUE) {
932 0 : key = uenum_next(keywordEnum, NULL, status);
933 0 : if (key == NULL) {
934 0 : break;
935 : }
936 0 : len = uloc_getKeywordValue(localeID, key, buf, sizeof(buf), &tmpStatus);
937 : /* buf must be null-terminated */
938 0 : if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) {
939 0 : if (strict) {
940 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
941 0 : break;
942 : }
943 : /* ignore this keyword */
944 0 : tmpStatus = U_ZERO_ERROR;
945 0 : continue;
946 : }
947 :
948 0 : keylen = (int32_t)uprv_strlen(key);
949 0 : isBcpUExt = (keylen > 1);
950 :
951 : /* special keyword used for representing Unicode locale attributes */
952 0 : if (uprv_strcmp(key, LOCALE_ATTRIBUTE_KEY) == 0) {
953 0 : if (len > 0) {
954 0 : int32_t i = 0;
955 : while (TRUE) {
956 0 : attrBufLength = 0;
957 0 : for (; i < len; i++) {
958 0 : if (buf[i] != '-') {
959 0 : attrBuf[attrBufLength++] = buf[i];
960 : } else {
961 0 : i++;
962 0 : break;
963 : }
964 : }
965 0 : if (attrBufLength > 0) {
966 0 : attrBuf[attrBufLength] = 0;
967 :
968 0 : } else if (i >= len){
969 0 : break;
970 : }
971 :
972 : /* create AttributeListEntry */
973 0 : attr = (AttributeListEntry*)uprv_malloc(sizeof(AttributeListEntry));
974 0 : if (attr == NULL) {
975 0 : *status = U_MEMORY_ALLOCATION_ERROR;
976 0 : break;
977 : }
978 0 : attrValue = (char*)uprv_malloc(attrBufLength + 1);
979 0 : if (attrValue == NULL) {
980 0 : *status = U_MEMORY_ALLOCATION_ERROR;
981 0 : break;
982 : }
983 0 : uprv_strcpy(attrValue, attrBuf);
984 0 : attr->attribute = attrValue;
985 :
986 0 : if (!_addAttributeToList(&firstAttr, attr)) {
987 0 : uprv_free(attr);
988 0 : uprv_free(attrValue);
989 0 : if (strict) {
990 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
991 0 : break;
992 : }
993 : }
994 : }
995 : /* for a place holder ExtensionListEntry */
996 0 : bcpKey = LOCALE_ATTRIBUTE_KEY;
997 0 : bcpValue = NULL;
998 : }
999 0 : } else if (isBcpUExt) {
1000 0 : bcpKey = uloc_toUnicodeLocaleKey(key);
1001 0 : if (bcpKey == NULL) {
1002 0 : if (strict) {
1003 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1004 0 : break;
1005 : }
1006 0 : continue;
1007 : }
1008 :
1009 : /* we've checked buf is null-terminated above */
1010 0 : bcpValue = uloc_toUnicodeLocaleType(key, buf);
1011 0 : if (bcpValue == NULL) {
1012 0 : if (strict) {
1013 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1014 0 : break;
1015 : }
1016 0 : continue;
1017 : }
1018 0 : if (bcpValue == buf) {
1019 : /*
1020 : When uloc_toUnicodeLocaleType(key, buf) returns the
1021 : input value as is, the value is well-formed, but has
1022 : no known mapping. This implementation normalizes the
1023 : the value to lower case
1024 : */
1025 0 : int32_t bcpValueLen = uprv_strlen(bcpValue);
1026 0 : if (bcpValueLen < extBufCapacity) {
1027 0 : uprv_strcpy(pExtBuf, bcpValue);
1028 0 : T_CString_toLowerCase(pExtBuf);
1029 :
1030 0 : bcpValue = pExtBuf;
1031 :
1032 0 : pExtBuf += (bcpValueLen + 1);
1033 0 : extBufCapacity -= (bcpValueLen + 1);
1034 : } else {
1035 0 : if (strict) {
1036 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1037 0 : break;
1038 : }
1039 0 : continue;
1040 : }
1041 : }
1042 : } else {
1043 0 : if (*key == PRIVATEUSE) {
1044 0 : if (!_isPrivateuseValueSubtags(buf, len)) {
1045 0 : if (strict) {
1046 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1047 0 : break;
1048 : }
1049 0 : continue;
1050 : }
1051 : } else {
1052 0 : if (!_isExtensionSingleton(key, keylen) || !_isExtensionSubtags(buf, len)) {
1053 0 : if (strict) {
1054 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1055 0 : break;
1056 : }
1057 0 : continue;
1058 : }
1059 : }
1060 0 : bcpKey = key;
1061 0 : if ((len + 1) < extBufCapacity) {
1062 0 : uprv_memcpy(pExtBuf, buf, len);
1063 0 : bcpValue = pExtBuf;
1064 :
1065 0 : pExtBuf += len;
1066 :
1067 0 : *pExtBuf = 0;
1068 0 : pExtBuf++;
1069 :
1070 0 : extBufCapacity -= (len + 1);
1071 : } else {
1072 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1073 0 : break;
1074 : }
1075 : }
1076 :
1077 : /* create ExtensionListEntry */
1078 0 : ext = (ExtensionListEntry*)uprv_malloc(sizeof(ExtensionListEntry));
1079 0 : if (ext == NULL) {
1080 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1081 0 : break;
1082 : }
1083 0 : ext->key = bcpKey;
1084 0 : ext->value = bcpValue;
1085 :
1086 0 : if (!_addExtensionToList(&firstExt, ext, TRUE)) {
1087 0 : uprv_free(ext);
1088 0 : if (strict) {
1089 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1090 0 : break;
1091 : }
1092 : }
1093 0 : }
1094 :
1095 : /* Special handling for POSIX variant - add the keywords for POSIX */
1096 0 : if (hadPosix) {
1097 : /* create ExtensionListEntry for POSIX */
1098 0 : ext = (ExtensionListEntry*)uprv_malloc(sizeof(ExtensionListEntry));
1099 0 : if (ext == NULL) {
1100 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1101 0 : goto cleanup;
1102 : }
1103 0 : ext->key = POSIX_KEY;
1104 0 : ext->value = POSIX_VALUE;
1105 :
1106 0 : if (!_addExtensionToList(&firstExt, ext, TRUE)) {
1107 0 : uprv_free(ext);
1108 : }
1109 : }
1110 :
1111 0 : if (U_SUCCESS(*status) && (firstExt != NULL || firstAttr != NULL)) {
1112 0 : UBool startLDMLExtension = FALSE;
1113 0 : for (ext = firstExt; ext; ext = ext->next) {
1114 0 : if (!startLDMLExtension && uprv_strlen(ext->key) > 1) {
1115 : /* first LDML u singlton extension */
1116 0 : if (reslen < capacity) {
1117 0 : *(appendAt + reslen) = SEP;
1118 : }
1119 0 : reslen++;
1120 0 : if (reslen < capacity) {
1121 0 : *(appendAt + reslen) = LDMLEXT;
1122 : }
1123 0 : reslen++;
1124 :
1125 0 : startLDMLExtension = TRUE;
1126 : }
1127 :
1128 : /* write out the sorted BCP47 attributes, extensions and private use */
1129 0 : if (uprv_strcmp(ext->key, LOCALE_ATTRIBUTE_KEY) == 0) {
1130 : /* write the value for the attributes */
1131 0 : for (attr = firstAttr; attr; attr = attr->next) {
1132 0 : if (reslen < capacity) {
1133 0 : *(appendAt + reslen) = SEP;
1134 : }
1135 0 : reslen++;
1136 0 : len = (int32_t)uprv_strlen(attr->attribute);
1137 0 : if (reslen < capacity) {
1138 0 : uprv_memcpy(appendAt + reslen, attr->attribute, uprv_min(len, capacity - reslen));
1139 : }
1140 0 : reslen += len;
1141 : }
1142 : } else {
1143 0 : if (reslen < capacity) {
1144 0 : *(appendAt + reslen) = SEP;
1145 : }
1146 0 : reslen++;
1147 0 : len = (int32_t)uprv_strlen(ext->key);
1148 0 : if (reslen < capacity) {
1149 0 : uprv_memcpy(appendAt + reslen, ext->key, uprv_min(len, capacity - reslen));
1150 : }
1151 0 : reslen += len;
1152 0 : if (reslen < capacity) {
1153 0 : *(appendAt + reslen) = SEP;
1154 : }
1155 0 : reslen++;
1156 0 : len = (int32_t)uprv_strlen(ext->value);
1157 0 : if (reslen < capacity) {
1158 0 : uprv_memcpy(appendAt + reslen, ext->value, uprv_min(len, capacity - reslen));
1159 : }
1160 0 : reslen += len;
1161 : }
1162 : }
1163 : }
1164 : cleanup:
1165 : /* clean up */
1166 0 : ext = firstExt;
1167 0 : while (ext != NULL) {
1168 0 : ExtensionListEntry *tmpExt = ext->next;
1169 0 : uprv_free(ext);
1170 0 : ext = tmpExt;
1171 : }
1172 :
1173 0 : attr = firstAttr;
1174 0 : while (attr != NULL) {
1175 0 : AttributeListEntry *tmpAttr = attr->next;
1176 0 : char *pValue = (char *)attr->attribute;
1177 0 : uprv_free(pValue);
1178 0 : uprv_free(attr);
1179 0 : attr = tmpAttr;
1180 : }
1181 :
1182 0 : uenum_close(keywordEnum);
1183 :
1184 0 : if (U_FAILURE(*status)) {
1185 0 : return 0;
1186 : }
1187 : }
1188 :
1189 11 : return u_terminateChars(appendAt, capacity, reslen, status);
1190 : }
1191 :
1192 : /**
1193 : * Append keywords parsed from LDML extension value
1194 : * e.g. "u-ca-gregory-co-trad" -> {calendar = gregorian} {collation = traditional}
1195 : * Note: char* buf is used for storing keywords
1196 : */
1197 : static void
1198 0 : _appendLDMLExtensionAsKeywords(const char* ldmlext, ExtensionListEntry** appendTo, char* buf, int32_t bufSize, UBool *posixVariant, UErrorCode *status) {
1199 : const char *pTag; /* beginning of current subtag */
1200 : const char *pKwds; /* beginning of key-type pairs */
1201 0 : UBool variantExists = *posixVariant;
1202 :
1203 0 : ExtensionListEntry *kwdFirst = NULL; /* first LDML keyword */
1204 : ExtensionListEntry *kwd, *nextKwd;
1205 :
1206 0 : AttributeListEntry *attrFirst = NULL; /* first attribute */
1207 : AttributeListEntry *attr, *nextAttr;
1208 :
1209 : int32_t len;
1210 0 : int32_t bufIdx = 0;
1211 :
1212 : char attrBuf[ULOC_KEYWORD_AND_VALUES_CAPACITY];
1213 0 : int32_t attrBufIdx = 0;
1214 :
1215 : /* Reset the posixVariant value */
1216 0 : *posixVariant = FALSE;
1217 :
1218 0 : pTag = ldmlext;
1219 0 : pKwds = NULL;
1220 :
1221 : /* Iterate through u extension attributes */
1222 0 : while (*pTag) {
1223 : /* locate next separator char */
1224 0 : for (len = 0; *(pTag + len) && *(pTag + len) != SEP; len++);
1225 :
1226 0 : if (ultag_isUnicodeLocaleKey(pTag, len)) {
1227 0 : pKwds = pTag;
1228 0 : break;
1229 : }
1230 :
1231 : /* add this attribute to the list */
1232 0 : attr = (AttributeListEntry*)uprv_malloc(sizeof(AttributeListEntry));
1233 0 : if (attr == NULL) {
1234 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1235 0 : goto cleanup;
1236 : }
1237 :
1238 0 : if (len < (int32_t)sizeof(attrBuf) - attrBufIdx) {
1239 0 : uprv_memcpy(&attrBuf[attrBufIdx], pTag, len);
1240 0 : attrBuf[attrBufIdx + len] = 0;
1241 0 : attr->attribute = &attrBuf[attrBufIdx];
1242 0 : attrBufIdx += (len + 1);
1243 : } else {
1244 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1245 0 : goto cleanup;
1246 : }
1247 :
1248 0 : if (!_addAttributeToList(&attrFirst, attr)) {
1249 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1250 0 : uprv_free(attr);
1251 0 : goto cleanup;
1252 : }
1253 :
1254 : /* next tag */
1255 0 : pTag += len;
1256 0 : if (*pTag) {
1257 : /* next to the separator */
1258 0 : pTag++;
1259 : }
1260 : }
1261 :
1262 0 : if (attrFirst) {
1263 : /* emit attributes as an LDML keyword, e.g. attribute=attr1-attr2 */
1264 :
1265 0 : if (attrBufIdx > bufSize) {
1266 : /* attrBufIdx == <total length of attribute subtag> + 1 */
1267 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1268 0 : goto cleanup;
1269 : }
1270 :
1271 0 : kwd = (ExtensionListEntry*)uprv_malloc(sizeof(ExtensionListEntry));
1272 0 : if (kwd == NULL) {
1273 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1274 0 : goto cleanup;
1275 : }
1276 :
1277 0 : kwd->key = LOCALE_ATTRIBUTE_KEY;
1278 0 : kwd->value = buf;
1279 :
1280 : /* attribute subtags sorted in alphabetical order as type */
1281 0 : attr = attrFirst;
1282 0 : while (attr != NULL) {
1283 0 : nextAttr = attr->next;
1284 :
1285 : /* buffer size check is done above */
1286 0 : if (attr != attrFirst) {
1287 0 : *(buf + bufIdx) = SEP;
1288 0 : bufIdx++;
1289 : }
1290 :
1291 0 : len = uprv_strlen(attr->attribute);
1292 0 : uprv_memcpy(buf + bufIdx, attr->attribute, len);
1293 0 : bufIdx += len;
1294 :
1295 0 : attr = nextAttr;
1296 : }
1297 0 : *(buf + bufIdx) = 0;
1298 0 : bufIdx++;
1299 :
1300 0 : if (!_addExtensionToList(&kwdFirst, kwd, FALSE)) {
1301 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1302 0 : uprv_free(kwd);
1303 0 : goto cleanup;
1304 : }
1305 :
1306 : /* once keyword entry is created, delete the attribute list */
1307 0 : attr = attrFirst;
1308 0 : while (attr != NULL) {
1309 0 : nextAttr = attr->next;
1310 0 : uprv_free(attr);
1311 0 : attr = nextAttr;
1312 : }
1313 0 : attrFirst = NULL;
1314 : }
1315 :
1316 0 : if (pKwds) {
1317 0 : const char *pBcpKey = NULL; /* u extenstion key subtag */
1318 0 : const char *pBcpType = NULL; /* beginning of u extension type subtag(s) */
1319 0 : int32_t bcpKeyLen = 0;
1320 0 : int32_t bcpTypeLen = 0;
1321 0 : UBool isDone = FALSE;
1322 :
1323 0 : pTag = pKwds;
1324 : /* BCP47 representation of LDML key/type pairs */
1325 0 : while (!isDone) {
1326 0 : const char *pNextBcpKey = NULL;
1327 0 : int32_t nextBcpKeyLen = 0;
1328 0 : UBool emitKeyword = FALSE;
1329 :
1330 0 : if (*pTag) {
1331 : /* locate next separator char */
1332 0 : for (len = 0; *(pTag + len) && *(pTag + len) != SEP; len++);
1333 :
1334 0 : if (ultag_isUnicodeLocaleKey(pTag, len)) {
1335 0 : if (pBcpKey) {
1336 0 : emitKeyword = TRUE;
1337 0 : pNextBcpKey = pTag;
1338 0 : nextBcpKeyLen = len;
1339 : } else {
1340 0 : pBcpKey = pTag;
1341 0 : bcpKeyLen = len;
1342 : }
1343 : } else {
1344 0 : U_ASSERT(pBcpKey != NULL);
1345 : /* within LDML type subtags */
1346 0 : if (pBcpType) {
1347 0 : bcpTypeLen += (len + 1);
1348 : } else {
1349 0 : pBcpType = pTag;
1350 0 : bcpTypeLen = len;
1351 : }
1352 : }
1353 :
1354 : /* next tag */
1355 0 : pTag += len;
1356 0 : if (*pTag) {
1357 : /* next to the separator */
1358 0 : pTag++;
1359 : }
1360 : } else {
1361 : /* processing last one */
1362 0 : emitKeyword = TRUE;
1363 0 : isDone = TRUE;
1364 : }
1365 :
1366 0 : if (emitKeyword) {
1367 0 : const char *pKey = NULL; /* LDML key */
1368 0 : const char *pType = NULL; /* LDML type */
1369 :
1370 : char bcpKeyBuf[9]; /* BCP key length is always 2 for now */
1371 :
1372 0 : U_ASSERT(pBcpKey != NULL);
1373 :
1374 0 : if (bcpKeyLen >= (int32_t)sizeof(bcpKeyBuf)) {
1375 : /* the BCP key is invalid */
1376 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1377 0 : goto cleanup;
1378 : }
1379 :
1380 0 : uprv_strncpy(bcpKeyBuf, pBcpKey, bcpKeyLen);
1381 0 : bcpKeyBuf[bcpKeyLen] = 0;
1382 :
1383 : /* u extension key to LDML key */
1384 0 : pKey = uloc_toLegacyKey(bcpKeyBuf);
1385 0 : if (pKey == NULL) {
1386 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1387 0 : goto cleanup;
1388 : }
1389 0 : if (pKey == bcpKeyBuf) {
1390 : /*
1391 : The key returned by toLegacyKey points to the input buffer.
1392 : We normalize the result key to lower case.
1393 : */
1394 0 : T_CString_toLowerCase(bcpKeyBuf);
1395 0 : if (bufSize - bufIdx - 1 >= bcpKeyLen) {
1396 0 : uprv_memcpy(buf + bufIdx, bcpKeyBuf, bcpKeyLen);
1397 0 : pKey = buf + bufIdx;
1398 0 : bufIdx += bcpKeyLen;
1399 0 : *(buf + bufIdx) = 0;
1400 0 : bufIdx++;
1401 : } else {
1402 0 : *status = U_BUFFER_OVERFLOW_ERROR;
1403 0 : goto cleanup;
1404 : }
1405 : }
1406 :
1407 0 : if (pBcpType) {
1408 : char bcpTypeBuf[128]; /* practically long enough even considering multiple subtag type */
1409 0 : if (bcpTypeLen >= (int32_t)sizeof(bcpTypeBuf)) {
1410 : /* the BCP type is too long */
1411 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1412 0 : goto cleanup;
1413 : }
1414 :
1415 0 : uprv_strncpy(bcpTypeBuf, pBcpType, bcpTypeLen);
1416 0 : bcpTypeBuf[bcpTypeLen] = 0;
1417 :
1418 : /* BCP type to locale type */
1419 0 : pType = uloc_toLegacyType(pKey, bcpTypeBuf);
1420 0 : if (pType == NULL) {
1421 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1422 0 : goto cleanup;
1423 : }
1424 0 : if (pType == bcpTypeBuf) {
1425 : /*
1426 : The type returned by toLegacyType points to the input buffer.
1427 : We normalize the result type to lower case.
1428 : */
1429 : /* normalize to lower case */
1430 0 : T_CString_toLowerCase(bcpTypeBuf);
1431 0 : if (bufSize - bufIdx - 1 >= bcpTypeLen) {
1432 0 : uprv_memcpy(buf + bufIdx, bcpTypeBuf, bcpTypeLen);
1433 0 : pType = buf + bufIdx;
1434 0 : bufIdx += bcpTypeLen;
1435 0 : *(buf + bufIdx) = 0;
1436 0 : bufIdx++;
1437 : } else {
1438 0 : *status = U_BUFFER_OVERFLOW_ERROR;
1439 0 : goto cleanup;
1440 : }
1441 : }
1442 : } else {
1443 : /* typeless - default type value is "yes" */
1444 0 : pType = LOCALE_TYPE_YES;
1445 : }
1446 :
1447 : /* Special handling for u-va-posix, since we want to treat this as a variant,
1448 : not as a keyword */
1449 0 : if (!variantExists && !uprv_strcmp(pKey, POSIX_KEY) && !uprv_strcmp(pType, POSIX_VALUE) ) {
1450 0 : *posixVariant = TRUE;
1451 : } else {
1452 : /* create an ExtensionListEntry for this keyword */
1453 0 : kwd = (ExtensionListEntry*)uprv_malloc(sizeof(ExtensionListEntry));
1454 0 : if (kwd == NULL) {
1455 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1456 0 : goto cleanup;
1457 : }
1458 :
1459 0 : kwd->key = pKey;
1460 0 : kwd->value = pType;
1461 :
1462 0 : if (!_addExtensionToList(&kwdFirst, kwd, FALSE)) {
1463 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1464 0 : uprv_free(kwd);
1465 0 : goto cleanup;
1466 : }
1467 : }
1468 :
1469 0 : pBcpKey = pNextBcpKey;
1470 0 : bcpKeyLen = pNextBcpKey != NULL ? nextBcpKeyLen : 0;
1471 0 : pBcpType = NULL;
1472 0 : bcpTypeLen = 0;
1473 : }
1474 : }
1475 : }
1476 :
1477 0 : kwd = kwdFirst;
1478 0 : while (kwd != NULL) {
1479 0 : nextKwd = kwd->next;
1480 0 : _addExtensionToList(appendTo, kwd, FALSE);
1481 0 : kwd = nextKwd;
1482 : }
1483 :
1484 0 : return;
1485 :
1486 : cleanup:
1487 0 : attr = attrFirst;
1488 0 : while (attr != NULL) {
1489 0 : nextAttr = attr->next;
1490 0 : uprv_free(attr);
1491 0 : attr = nextAttr;
1492 : }
1493 :
1494 0 : kwd = kwdFirst;
1495 0 : while (kwd != NULL) {
1496 0 : nextKwd = kwd->next;
1497 0 : uprv_free(kwd);
1498 0 : kwd = nextKwd;
1499 : }
1500 : }
1501 :
1502 :
1503 : static int32_t
1504 0 : _appendKeywords(ULanguageTag* langtag, char* appendAt, int32_t capacity, UErrorCode* status) {
1505 0 : int32_t reslen = 0;
1506 : int32_t i, n;
1507 : int32_t len;
1508 0 : ExtensionListEntry *kwdFirst = NULL;
1509 : ExtensionListEntry *kwd;
1510 : const char *key, *type;
1511 0 : char *kwdBuf = NULL;
1512 0 : int32_t kwdBufLength = capacity;
1513 0 : UBool posixVariant = FALSE;
1514 :
1515 0 : if (U_FAILURE(*status)) {
1516 0 : return 0;
1517 : }
1518 :
1519 0 : kwdBuf = (char*)uprv_malloc(kwdBufLength);
1520 0 : if (kwdBuf == NULL) {
1521 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1522 0 : return 0;
1523 : }
1524 :
1525 : /* Determine if variants already exists */
1526 0 : if (ultag_getVariantsSize(langtag)) {
1527 0 : posixVariant = TRUE;
1528 : }
1529 :
1530 0 : n = ultag_getExtensionsSize(langtag);
1531 :
1532 : /* resolve locale keywords and reordering keys */
1533 0 : for (i = 0; i < n; i++) {
1534 0 : key = ultag_getExtensionKey(langtag, i);
1535 0 : type = ultag_getExtensionValue(langtag, i);
1536 0 : if (*key == LDMLEXT) {
1537 0 : _appendLDMLExtensionAsKeywords(type, &kwdFirst, kwdBuf, kwdBufLength, &posixVariant, status);
1538 0 : if (U_FAILURE(*status)) {
1539 0 : break;
1540 : }
1541 : } else {
1542 0 : kwd = (ExtensionListEntry*)uprv_malloc(sizeof(ExtensionListEntry));
1543 0 : if (kwd == NULL) {
1544 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1545 0 : break;
1546 : }
1547 0 : kwd->key = key;
1548 0 : kwd->value = type;
1549 0 : if (!_addExtensionToList(&kwdFirst, kwd, FALSE)) {
1550 0 : uprv_free(kwd);
1551 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1552 0 : break;
1553 : }
1554 : }
1555 : }
1556 :
1557 0 : if (U_SUCCESS(*status)) {
1558 0 : type = ultag_getPrivateUse(langtag);
1559 0 : if ((int32_t)uprv_strlen(type) > 0) {
1560 : /* add private use as a keyword */
1561 0 : kwd = (ExtensionListEntry*)uprv_malloc(sizeof(ExtensionListEntry));
1562 0 : if (kwd == NULL) {
1563 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1564 : } else {
1565 0 : kwd->key = PRIVATEUSE_KEY;
1566 0 : kwd->value = type;
1567 0 : if (!_addExtensionToList(&kwdFirst, kwd, FALSE)) {
1568 0 : uprv_free(kwd);
1569 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1570 : }
1571 : }
1572 : }
1573 : }
1574 :
1575 : /* If a POSIX variant was in the extensions, write it out before writing the keywords. */
1576 :
1577 0 : if (U_SUCCESS(*status) && posixVariant) {
1578 0 : len = (int32_t) uprv_strlen(_POSIX);
1579 0 : if (reslen < capacity) {
1580 0 : uprv_memcpy(appendAt + reslen, _POSIX, uprv_min(len, capacity - reslen));
1581 : }
1582 0 : reslen += len;
1583 : }
1584 :
1585 0 : if (U_SUCCESS(*status) && kwdFirst != NULL) {
1586 : /* write out the sorted keywords */
1587 0 : UBool firstValue = TRUE;
1588 0 : kwd = kwdFirst;
1589 0 : do {
1590 0 : if (reslen < capacity) {
1591 0 : if (firstValue) {
1592 : /* '@' */
1593 0 : *(appendAt + reslen) = LOCALE_EXT_SEP;
1594 0 : firstValue = FALSE;
1595 : } else {
1596 : /* ';' */
1597 0 : *(appendAt + reslen) = LOCALE_KEYWORD_SEP;
1598 : }
1599 : }
1600 0 : reslen++;
1601 :
1602 : /* key */
1603 0 : len = (int32_t)uprv_strlen(kwd->key);
1604 0 : if (reslen < capacity) {
1605 0 : uprv_memcpy(appendAt + reslen, kwd->key, uprv_min(len, capacity - reslen));
1606 : }
1607 0 : reslen += len;
1608 :
1609 : /* '=' */
1610 0 : if (reslen < capacity) {
1611 0 : *(appendAt + reslen) = LOCALE_KEY_TYPE_SEP;
1612 : }
1613 0 : reslen++;
1614 :
1615 : /* type */
1616 0 : len = (int32_t)uprv_strlen(kwd->value);
1617 0 : if (reslen < capacity) {
1618 0 : uprv_memcpy(appendAt + reslen, kwd->value, uprv_min(len, capacity - reslen));
1619 : }
1620 0 : reslen += len;
1621 :
1622 0 : kwd = kwd->next;
1623 0 : } while (kwd);
1624 : }
1625 :
1626 : /* clean up */
1627 0 : kwd = kwdFirst;
1628 0 : while (kwd != NULL) {
1629 0 : ExtensionListEntry *tmpKwd = kwd->next;
1630 0 : uprv_free(kwd);
1631 0 : kwd = tmpKwd;
1632 : }
1633 :
1634 0 : uprv_free(kwdBuf);
1635 :
1636 0 : if (U_FAILURE(*status)) {
1637 0 : return 0;
1638 : }
1639 :
1640 0 : return u_terminateChars(appendAt, capacity, reslen, status);
1641 : }
1642 :
1643 : static int32_t
1644 11 : _appendPrivateuseToLanguageTag(const char* localeID, char* appendAt, int32_t capacity, UBool strict, UBool hadPosix, UErrorCode* status) {
1645 : (void)hadPosix;
1646 : char buf[ULOC_FULLNAME_CAPACITY];
1647 : char tmpAppend[ULOC_FULLNAME_CAPACITY];
1648 11 : UErrorCode tmpStatus = U_ZERO_ERROR;
1649 : int32_t len, i;
1650 11 : int32_t reslen = 0;
1651 :
1652 11 : if (U_FAILURE(*status)) {
1653 0 : return 0;
1654 : }
1655 :
1656 11 : len = uloc_getVariant(localeID, buf, sizeof(buf), &tmpStatus);
1657 11 : if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) {
1658 0 : if (strict) {
1659 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1660 : }
1661 0 : return 0;
1662 : }
1663 :
1664 11 : if (len > 0) {
1665 : char *p, *pPriv;
1666 0 : UBool bNext = TRUE;
1667 0 : UBool firstValue = TRUE;
1668 : UBool writeValue;
1669 :
1670 0 : pPriv = NULL;
1671 0 : p = buf;
1672 0 : while (bNext) {
1673 0 : writeValue = FALSE;
1674 0 : if (*p == SEP || *p == LOCALE_SEP || *p == 0) {
1675 0 : if (*p == 0) {
1676 0 : bNext = FALSE;
1677 : } else {
1678 0 : *p = 0; /* terminate */
1679 : }
1680 0 : if (pPriv != NULL) {
1681 : /* Private use in the canonical format is lowercase in BCP47 */
1682 0 : for (i = 0; *(pPriv + i) != 0; i++) {
1683 0 : *(pPriv + i) = uprv_tolower(*(pPriv + i));
1684 : }
1685 :
1686 : /* validate */
1687 0 : if (_isPrivateuseValueSubtag(pPriv, -1)) {
1688 0 : if (firstValue) {
1689 0 : if (!_isVariantSubtag(pPriv, -1)) {
1690 0 : writeValue = TRUE;
1691 : }
1692 : } else {
1693 0 : writeValue = TRUE;
1694 : }
1695 0 : } else if (strict) {
1696 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1697 0 : break;
1698 : } else {
1699 0 : break;
1700 : }
1701 :
1702 0 : if (writeValue) {
1703 0 : if (reslen < capacity) {
1704 0 : tmpAppend[reslen++] = SEP;
1705 : }
1706 :
1707 0 : if (firstValue) {
1708 0 : if (reslen < capacity) {
1709 0 : tmpAppend[reslen++] = *PRIVATEUSE_KEY;
1710 : }
1711 :
1712 0 : if (reslen < capacity) {
1713 0 : tmpAppend[reslen++] = SEP;
1714 : }
1715 :
1716 0 : len = (int32_t)uprv_strlen(PRIVUSE_VARIANT_PREFIX);
1717 0 : if (reslen < capacity) {
1718 0 : uprv_memcpy(tmpAppend + reslen, PRIVUSE_VARIANT_PREFIX, uprv_min(len, capacity - reslen));
1719 : }
1720 0 : reslen += len;
1721 :
1722 0 : if (reslen < capacity) {
1723 0 : tmpAppend[reslen++] = SEP;
1724 : }
1725 :
1726 0 : firstValue = FALSE;
1727 : }
1728 :
1729 0 : len = (int32_t)uprv_strlen(pPriv);
1730 0 : if (reslen < capacity) {
1731 0 : uprv_memcpy(tmpAppend + reslen, pPriv, uprv_min(len, capacity - reslen));
1732 : }
1733 0 : reslen += len;
1734 : }
1735 : }
1736 : /* reset private use starting position */
1737 0 : pPriv = NULL;
1738 0 : } else if (pPriv == NULL) {
1739 0 : pPriv = p;
1740 : }
1741 0 : p++;
1742 : }
1743 :
1744 0 : if (U_FAILURE(*status)) {
1745 0 : return 0;
1746 : }
1747 : }
1748 :
1749 11 : if (U_SUCCESS(*status)) {
1750 11 : len = reslen;
1751 11 : if (reslen < capacity) {
1752 11 : uprv_memcpy(appendAt, tmpAppend, uprv_min(len, capacity - reslen));
1753 : }
1754 : }
1755 :
1756 11 : u_terminateChars(appendAt, capacity, reslen, status);
1757 :
1758 11 : return reslen;
1759 : }
1760 :
1761 : /*
1762 : * -------------------------------------------------
1763 : *
1764 : * ultag_ functions
1765 : *
1766 : * -------------------------------------------------
1767 : */
1768 :
1769 : /* Bit flags used by the parser */
1770 : #define LANG 0x0001
1771 : #define EXTL 0x0002
1772 : #define SCRT 0x0004
1773 : #define REGN 0x0008
1774 : #define VART 0x0010
1775 : #define EXTS 0x0020
1776 : #define EXTV 0x0040
1777 : #define PRIV 0x0080
1778 :
1779 : /**
1780 : * Ticket #12705 - Visual Studio 2015 Update 3 contains a new code optimizer which has problems optimizing
1781 : * this function. (See https://blogs.msdn.microsoft.com/vcblog/2016/05/04/new-code-optimizer/ )
1782 : * As a workaround, we will turn off optimization just for this function on VS2015 Update 3 and above.
1783 : */
1784 : #if (defined(_MSC_VER) && (_MSC_VER >= 1900) && defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190024210))
1785 : #pragma optimize( "", off )
1786 : #endif
1787 :
1788 : static ULanguageTag*
1789 0 : ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* status) {
1790 : ULanguageTag *t;
1791 : char *tagBuf;
1792 : int16_t next;
1793 : char *pSubtag, *pNext, *pLastGoodPosition;
1794 : int32_t subtagLen;
1795 : int32_t extlangIdx;
1796 : ExtensionListEntry *pExtension;
1797 : char *pExtValueSubtag, *pExtValueSubtagEnd;
1798 : int32_t i;
1799 0 : UBool privateuseVar = FALSE;
1800 0 : int32_t grandfatheredLen = 0;
1801 :
1802 0 : if (parsedLen != NULL) {
1803 0 : *parsedLen = 0;
1804 : }
1805 :
1806 0 : if (U_FAILURE(*status)) {
1807 0 : return NULL;
1808 : }
1809 :
1810 0 : if (tagLen < 0) {
1811 0 : tagLen = (int32_t)uprv_strlen(tag);
1812 : }
1813 :
1814 : /* copy the entire string */
1815 0 : tagBuf = (char*)uprv_malloc(tagLen + 1);
1816 0 : if (tagBuf == NULL) {
1817 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1818 0 : return NULL;
1819 : }
1820 0 : uprv_memcpy(tagBuf, tag, tagLen);
1821 0 : *(tagBuf + tagLen) = 0;
1822 :
1823 : /* create a ULanguageTag */
1824 0 : t = (ULanguageTag*)uprv_malloc(sizeof(ULanguageTag));
1825 0 : if (t == NULL) {
1826 0 : uprv_free(tagBuf);
1827 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1828 0 : return NULL;
1829 : }
1830 0 : _initializeULanguageTag(t);
1831 0 : t->buf = tagBuf;
1832 :
1833 0 : if (tagLen < MINLEN) {
1834 : /* the input tag is too short - return empty ULanguageTag */
1835 0 : return t;
1836 : }
1837 :
1838 : /* check if the tag is grandfathered */
1839 0 : for (i = 0; GRANDFATHERED[i] != NULL; i += 2) {
1840 0 : if (uprv_stricmp(GRANDFATHERED[i], tagBuf) == 0) {
1841 : int32_t newTagLength;
1842 :
1843 0 : grandfatheredLen = tagLen; /* back up for output parsedLen */
1844 0 : newTagLength = uprv_strlen(GRANDFATHERED[i+1]);
1845 0 : if (tagLen < newTagLength) {
1846 0 : uprv_free(tagBuf);
1847 0 : tagBuf = (char*)uprv_malloc(newTagLength + 1);
1848 0 : if (tagBuf == NULL) {
1849 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1850 0 : ultag_close(t);
1851 0 : return NULL;
1852 : }
1853 0 : t->buf = tagBuf;
1854 0 : tagLen = newTagLength;
1855 : }
1856 0 : uprv_strcpy(t->buf, GRANDFATHERED[i + 1]);
1857 0 : break;
1858 : }
1859 : }
1860 :
1861 : /*
1862 : * langtag = language
1863 : * ["-" script]
1864 : * ["-" region]
1865 : * *("-" variant)
1866 : * *("-" extension)
1867 : * ["-" privateuse]
1868 : */
1869 :
1870 0 : next = LANG | PRIV;
1871 0 : pNext = pLastGoodPosition = tagBuf;
1872 0 : extlangIdx = 0;
1873 0 : pExtension = NULL;
1874 0 : pExtValueSubtag = NULL;
1875 0 : pExtValueSubtagEnd = NULL;
1876 :
1877 0 : while (pNext) {
1878 : char *pSep;
1879 :
1880 0 : pSubtag = pNext;
1881 :
1882 : /* locate next separator char */
1883 0 : pSep = pSubtag;
1884 0 : while (*pSep) {
1885 0 : if (*pSep == SEP) {
1886 0 : break;
1887 : }
1888 0 : pSep++;
1889 : }
1890 0 : if (*pSep == 0) {
1891 : /* last subtag */
1892 0 : pNext = NULL;
1893 : } else {
1894 0 : pNext = pSep + 1;
1895 : }
1896 0 : subtagLen = (int32_t)(pSep - pSubtag);
1897 :
1898 0 : if (next & LANG) {
1899 0 : if (_isLanguageSubtag(pSubtag, subtagLen)) {
1900 0 : *pSep = 0; /* terminate */
1901 0 : t->language = T_CString_toLowerCase(pSubtag);
1902 :
1903 0 : pLastGoodPosition = pSep;
1904 0 : next = EXTL | SCRT | REGN | VART | EXTS | PRIV;
1905 0 : continue;
1906 : }
1907 : }
1908 0 : if (next & EXTL) {
1909 0 : if (_isExtlangSubtag(pSubtag, subtagLen)) {
1910 0 : *pSep = 0;
1911 0 : t->extlang[extlangIdx++] = T_CString_toLowerCase(pSubtag);
1912 :
1913 0 : pLastGoodPosition = pSep;
1914 0 : if (extlangIdx < 3) {
1915 0 : next = EXTL | SCRT | REGN | VART | EXTS | PRIV;
1916 : } else {
1917 0 : next = SCRT | REGN | VART | EXTS | PRIV;
1918 : }
1919 0 : continue;
1920 : }
1921 : }
1922 0 : if (next & SCRT) {
1923 0 : if (_isScriptSubtag(pSubtag, subtagLen)) {
1924 0 : char *p = pSubtag;
1925 :
1926 0 : *pSep = 0;
1927 :
1928 : /* to title case */
1929 0 : *p = uprv_toupper(*p);
1930 0 : p++;
1931 0 : for (; *p; p++) {
1932 0 : *p = uprv_tolower(*p);
1933 : }
1934 :
1935 0 : t->script = pSubtag;
1936 :
1937 0 : pLastGoodPosition = pSep;
1938 0 : next = REGN | VART | EXTS | PRIV;
1939 0 : continue;
1940 : }
1941 : }
1942 0 : if (next & REGN) {
1943 0 : if (_isRegionSubtag(pSubtag, subtagLen)) {
1944 0 : *pSep = 0;
1945 0 : t->region = T_CString_toUpperCase(pSubtag);
1946 :
1947 0 : pLastGoodPosition = pSep;
1948 0 : next = VART | EXTS | PRIV;
1949 0 : continue;
1950 : }
1951 : }
1952 0 : if (next & VART) {
1953 0 : if (_isVariantSubtag(pSubtag, subtagLen) ||
1954 0 : (privateuseVar && _isPrivateuseVariantSubtag(pSubtag, subtagLen))) {
1955 : VariantListEntry *var;
1956 : UBool isAdded;
1957 :
1958 0 : var = (VariantListEntry*)uprv_malloc(sizeof(VariantListEntry));
1959 0 : if (var == NULL) {
1960 0 : *status = U_MEMORY_ALLOCATION_ERROR;
1961 0 : goto error;
1962 : }
1963 0 : *pSep = 0;
1964 0 : var->variant = T_CString_toUpperCase(pSubtag);
1965 0 : isAdded = _addVariantToList(&(t->variants), var);
1966 0 : if (!isAdded) {
1967 : /* duplicated variant entry */
1968 0 : uprv_free(var);
1969 0 : break;
1970 : }
1971 0 : pLastGoodPosition = pSep;
1972 0 : next = VART | EXTS | PRIV;
1973 0 : continue;
1974 : }
1975 : }
1976 0 : if (next & EXTS) {
1977 0 : if (_isExtensionSingleton(pSubtag, subtagLen)) {
1978 0 : if (pExtension != NULL) {
1979 0 : if (pExtValueSubtag == NULL || pExtValueSubtagEnd == NULL) {
1980 : /* the previous extension is incomplete */
1981 0 : uprv_free(pExtension);
1982 0 : pExtension = NULL;
1983 0 : break;
1984 : }
1985 :
1986 : /* terminate the previous extension value */
1987 0 : *pExtValueSubtagEnd = 0;
1988 0 : pExtension->value = T_CString_toLowerCase(pExtValueSubtag);
1989 :
1990 : /* insert the extension to the list */
1991 0 : if (_addExtensionToList(&(t->extensions), pExtension, FALSE)) {
1992 0 : pLastGoodPosition = pExtValueSubtagEnd;
1993 : } else {
1994 : /* stop parsing here */
1995 0 : uprv_free(pExtension);
1996 0 : pExtension = NULL;
1997 0 : break;
1998 : }
1999 : }
2000 :
2001 : /* create a new extension */
2002 0 : pExtension = (ExtensionListEntry*)uprv_malloc(sizeof(ExtensionListEntry));
2003 0 : if (pExtension == NULL) {
2004 0 : *status = U_MEMORY_ALLOCATION_ERROR;
2005 0 : goto error;
2006 : }
2007 0 : *pSep = 0;
2008 0 : pExtension->key = T_CString_toLowerCase(pSubtag);
2009 0 : pExtension->value = NULL; /* will be set later */
2010 :
2011 : /*
2012 : * reset the start and the end location of extension value
2013 : * subtags for this extension
2014 : */
2015 0 : pExtValueSubtag = NULL;
2016 0 : pExtValueSubtagEnd = NULL;
2017 :
2018 0 : next = EXTV;
2019 0 : continue;
2020 : }
2021 : }
2022 0 : if (next & EXTV) {
2023 0 : if (_isExtensionSubtag(pSubtag, subtagLen)) {
2024 0 : if (pExtValueSubtag == NULL) {
2025 : /* if the start postion of this extension's value is not yet,
2026 : this one is the first value subtag */
2027 0 : pExtValueSubtag = pSubtag;
2028 : }
2029 :
2030 : /* Mark the end of this subtag */
2031 0 : pExtValueSubtagEnd = pSep;
2032 0 : next = EXTS | EXTV | PRIV;
2033 :
2034 0 : continue;
2035 : }
2036 : }
2037 0 : if (next & PRIV) {
2038 0 : if (uprv_tolower(*pSubtag) == PRIVATEUSE) {
2039 : char *pPrivuseVal;
2040 :
2041 0 : if (pExtension != NULL) {
2042 : /* Process the last extension */
2043 0 : if (pExtValueSubtag == NULL || pExtValueSubtagEnd == NULL) {
2044 : /* the previous extension is incomplete */
2045 0 : uprv_free(pExtension);
2046 0 : pExtension = NULL;
2047 0 : break;
2048 : } else {
2049 : /* terminate the previous extension value */
2050 0 : *pExtValueSubtagEnd = 0;
2051 0 : pExtension->value = T_CString_toLowerCase(pExtValueSubtag);
2052 :
2053 : /* insert the extension to the list */
2054 0 : if (_addExtensionToList(&(t->extensions), pExtension, FALSE)) {
2055 0 : pLastGoodPosition = pExtValueSubtagEnd;
2056 0 : pExtension = NULL;
2057 : } else {
2058 : /* stop parsing here */
2059 0 : uprv_free(pExtension);
2060 0 : pExtension = NULL;
2061 0 : break;
2062 : }
2063 : }
2064 : }
2065 :
2066 : /* The rest of part will be private use value subtags */
2067 0 : if (pNext == NULL) {
2068 : /* empty private use subtag */
2069 0 : break;
2070 : }
2071 : /* back up the private use value start position */
2072 0 : pPrivuseVal = pNext;
2073 :
2074 : /* validate private use value subtags */
2075 0 : while (pNext) {
2076 0 : pSubtag = pNext;
2077 0 : pSep = pSubtag;
2078 0 : while (*pSep) {
2079 0 : if (*pSep == SEP) {
2080 0 : break;
2081 : }
2082 0 : pSep++;
2083 : }
2084 0 : if (*pSep == 0) {
2085 : /* last subtag */
2086 0 : pNext = NULL;
2087 : } else {
2088 0 : pNext = pSep + 1;
2089 : }
2090 0 : subtagLen = (int32_t)(pSep - pSubtag);
2091 :
2092 0 : if (uprv_strncmp(pSubtag, PRIVUSE_VARIANT_PREFIX, uprv_strlen(PRIVUSE_VARIANT_PREFIX)) == 0) {
2093 0 : *pSep = 0;
2094 0 : next = VART;
2095 0 : privateuseVar = TRUE;
2096 0 : break;
2097 0 : } else if (_isPrivateuseValueSubtag(pSubtag, subtagLen)) {
2098 0 : pLastGoodPosition = pSep;
2099 : } else {
2100 0 : break;
2101 : }
2102 : }
2103 :
2104 0 : if (next == VART) {
2105 0 : continue;
2106 : }
2107 :
2108 0 : if (pLastGoodPosition - pPrivuseVal > 0) {
2109 0 : *pLastGoodPosition = 0;
2110 0 : t->privateuse = T_CString_toLowerCase(pPrivuseVal);
2111 : }
2112 : /* No more subtags, exiting the parse loop */
2113 0 : break;
2114 : }
2115 0 : break;
2116 : }
2117 :
2118 : /* If we fell through here, it means this subtag is illegal - quit parsing */
2119 0 : break;
2120 : }
2121 :
2122 0 : if (pExtension != NULL) {
2123 : /* Process the last extension */
2124 0 : if (pExtValueSubtag == NULL || pExtValueSubtagEnd == NULL) {
2125 : /* the previous extension is incomplete */
2126 0 : uprv_free(pExtension);
2127 : } else {
2128 : /* terminate the previous extension value */
2129 0 : *pExtValueSubtagEnd = 0;
2130 0 : pExtension->value = T_CString_toLowerCase(pExtValueSubtag);
2131 : /* insert the extension to the list */
2132 0 : if (_addExtensionToList(&(t->extensions), pExtension, FALSE)) {
2133 0 : pLastGoodPosition = pExtValueSubtagEnd;
2134 : } else {
2135 0 : uprv_free(pExtension);
2136 : }
2137 : }
2138 : }
2139 :
2140 0 : if (parsedLen != NULL) {
2141 0 : *parsedLen = (grandfatheredLen > 0) ? grandfatheredLen : (int32_t)(pLastGoodPosition - t->buf);
2142 : }
2143 :
2144 0 : return t;
2145 :
2146 : error:
2147 0 : ultag_close(t);
2148 0 : return NULL;
2149 : }
2150 :
2151 : /**
2152 : * Ticket #12705 - Turn optimization back on.
2153 : */
2154 : #if (defined(_MSC_VER) && (_MSC_VER >= 1900) && defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190024210))
2155 : #pragma optimize( "", on )
2156 : #endif
2157 :
2158 : static void
2159 0 : ultag_close(ULanguageTag* langtag) {
2160 :
2161 0 : if (langtag == NULL) {
2162 0 : return;
2163 : }
2164 :
2165 0 : uprv_free(langtag->buf);
2166 :
2167 0 : if (langtag->variants) {
2168 0 : VariantListEntry *curVar = langtag->variants;
2169 0 : while (curVar) {
2170 0 : VariantListEntry *nextVar = curVar->next;
2171 0 : uprv_free(curVar);
2172 0 : curVar = nextVar;
2173 : }
2174 : }
2175 :
2176 0 : if (langtag->extensions) {
2177 0 : ExtensionListEntry *curExt = langtag->extensions;
2178 0 : while (curExt) {
2179 0 : ExtensionListEntry *nextExt = curExt->next;
2180 0 : uprv_free(curExt);
2181 0 : curExt = nextExt;
2182 : }
2183 : }
2184 :
2185 0 : uprv_free(langtag);
2186 : }
2187 :
2188 : static const char*
2189 0 : ultag_getLanguage(const ULanguageTag* langtag) {
2190 0 : return langtag->language;
2191 : }
2192 :
2193 : #if 0
2194 : static const char*
2195 : ultag_getJDKLanguage(const ULanguageTag* langtag) {
2196 : int32_t i;
2197 : for (i = 0; DEPRECATEDLANGS[i] != NULL; i += 2) {
2198 : if (uprv_compareInvCharsAsAscii(DEPRECATEDLANGS[i], langtag->language) == 0) {
2199 : return DEPRECATEDLANGS[i + 1];
2200 : }
2201 : }
2202 : return langtag->language;
2203 : }
2204 : #endif
2205 :
2206 : static const char*
2207 0 : ultag_getExtlang(const ULanguageTag* langtag, int32_t idx) {
2208 0 : if (idx >= 0 && idx < MAXEXTLANG) {
2209 0 : return langtag->extlang[idx];
2210 : }
2211 0 : return NULL;
2212 : }
2213 :
2214 : static int32_t
2215 0 : ultag_getExtlangSize(const ULanguageTag* langtag) {
2216 0 : int32_t size = 0;
2217 : int32_t i;
2218 0 : for (i = 0; i < MAXEXTLANG; i++) {
2219 0 : if (langtag->extlang[i]) {
2220 0 : size++;
2221 : }
2222 : }
2223 0 : return size;
2224 : }
2225 :
2226 : static const char*
2227 0 : ultag_getScript(const ULanguageTag* langtag) {
2228 0 : return langtag->script;
2229 : }
2230 :
2231 : static const char*
2232 0 : ultag_getRegion(const ULanguageTag* langtag) {
2233 0 : return langtag->region;
2234 : }
2235 :
2236 : static const char*
2237 0 : ultag_getVariant(const ULanguageTag* langtag, int32_t idx) {
2238 0 : const char *var = NULL;
2239 0 : VariantListEntry *cur = langtag->variants;
2240 0 : int32_t i = 0;
2241 0 : while (cur) {
2242 0 : if (i == idx) {
2243 0 : var = cur->variant;
2244 0 : break;
2245 : }
2246 0 : cur = cur->next;
2247 0 : i++;
2248 : }
2249 0 : return var;
2250 : }
2251 :
2252 : static int32_t
2253 0 : ultag_getVariantsSize(const ULanguageTag* langtag) {
2254 0 : int32_t size = 0;
2255 0 : VariantListEntry *cur = langtag->variants;
2256 : while (TRUE) {
2257 0 : if (cur == NULL) {
2258 0 : break;
2259 : }
2260 0 : size++;
2261 0 : cur = cur->next;
2262 : }
2263 0 : return size;
2264 : }
2265 :
2266 : static const char*
2267 0 : ultag_getExtensionKey(const ULanguageTag* langtag, int32_t idx) {
2268 0 : const char *key = NULL;
2269 0 : ExtensionListEntry *cur = langtag->extensions;
2270 0 : int32_t i = 0;
2271 0 : while (cur) {
2272 0 : if (i == idx) {
2273 0 : key = cur->key;
2274 0 : break;
2275 : }
2276 0 : cur = cur->next;
2277 0 : i++;
2278 : }
2279 0 : return key;
2280 : }
2281 :
2282 : static const char*
2283 0 : ultag_getExtensionValue(const ULanguageTag* langtag, int32_t idx) {
2284 0 : const char *val = NULL;
2285 0 : ExtensionListEntry *cur = langtag->extensions;
2286 0 : int32_t i = 0;
2287 0 : while (cur) {
2288 0 : if (i == idx) {
2289 0 : val = cur->value;
2290 0 : break;
2291 : }
2292 0 : cur = cur->next;
2293 0 : i++;
2294 : }
2295 0 : return val;
2296 : }
2297 :
2298 : static int32_t
2299 0 : ultag_getExtensionsSize(const ULanguageTag* langtag) {
2300 0 : int32_t size = 0;
2301 0 : ExtensionListEntry *cur = langtag->extensions;
2302 : while (TRUE) {
2303 0 : if (cur == NULL) {
2304 0 : break;
2305 : }
2306 0 : size++;
2307 0 : cur = cur->next;
2308 : }
2309 0 : return size;
2310 : }
2311 :
2312 : static const char*
2313 0 : ultag_getPrivateUse(const ULanguageTag* langtag) {
2314 0 : return langtag->privateuse;
2315 : }
2316 :
2317 : #if 0
2318 : static const char*
2319 : ultag_getGrandfathered(const ULanguageTag* langtag) {
2320 : return langtag->grandfathered;
2321 : }
2322 : #endif
2323 :
2324 :
2325 : /*
2326 : * -------------------------------------------------
2327 : *
2328 : * Locale/BCP47 conversion APIs, exposed as uloc_*
2329 : *
2330 : * -------------------------------------------------
2331 : */
2332 : U_CAPI int32_t U_EXPORT2
2333 11 : uloc_toLanguageTag(const char* localeID,
2334 : char* langtag,
2335 : int32_t langtagCapacity,
2336 : UBool strict,
2337 : UErrorCode* status) {
2338 : /* char canonical[ULOC_FULLNAME_CAPACITY]; */ /* See #6822 */
2339 : char canonical[256];
2340 11 : int32_t reslen = 0;
2341 11 : UErrorCode tmpStatus = U_ZERO_ERROR;
2342 11 : UBool hadPosix = FALSE;
2343 : const char* pKeywordStart;
2344 :
2345 : /* Note: uloc_canonicalize returns "en_US_POSIX" for input locale ID "". See #6835 */
2346 11 : canonical[0] = 0;
2347 11 : if (uprv_strlen(localeID) > 0) {
2348 11 : uloc_canonicalize(localeID, canonical, sizeof(canonical), &tmpStatus);
2349 11 : if (tmpStatus != U_ZERO_ERROR) {
2350 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
2351 0 : return 0;
2352 : }
2353 : }
2354 :
2355 : /* For handling special case - private use only tag */
2356 11 : pKeywordStart = locale_getKeywordsStart(canonical);
2357 11 : if (pKeywordStart == canonical) {
2358 : UEnumeration *kwdEnum;
2359 0 : int kwdCnt = 0;
2360 0 : UBool done = FALSE;
2361 :
2362 0 : kwdEnum = uloc_openKeywords((const char*)canonical, &tmpStatus);
2363 0 : if (kwdEnum != NULL) {
2364 0 : kwdCnt = uenum_count(kwdEnum, &tmpStatus);
2365 0 : if (kwdCnt == 1) {
2366 : const char *key;
2367 0 : int32_t len = 0;
2368 :
2369 0 : key = uenum_next(kwdEnum, &len, &tmpStatus);
2370 0 : if (len == 1 && *key == PRIVATEUSE) {
2371 : char buf[ULOC_KEYWORD_AND_VALUES_CAPACITY];
2372 0 : buf[0] = PRIVATEUSE;
2373 0 : buf[1] = SEP;
2374 0 : len = uloc_getKeywordValue(localeID, key, &buf[2], sizeof(buf) - 2, &tmpStatus);
2375 0 : if (U_SUCCESS(tmpStatus)) {
2376 0 : if (_isPrivateuseValueSubtags(&buf[2], len)) {
2377 : /* return private use only tag */
2378 0 : reslen = len + 2;
2379 0 : uprv_memcpy(langtag, buf, uprv_min(reslen, langtagCapacity));
2380 0 : u_terminateChars(langtag, langtagCapacity, reslen, status);
2381 0 : done = TRUE;
2382 0 : } else if (strict) {
2383 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
2384 0 : done = TRUE;
2385 : }
2386 : /* if not strict mode, then "und" will be returned */
2387 : } else {
2388 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
2389 0 : done = TRUE;
2390 : }
2391 : }
2392 : }
2393 0 : uenum_close(kwdEnum);
2394 0 : if (done) {
2395 0 : return reslen;
2396 : }
2397 : }
2398 : }
2399 :
2400 11 : reslen += _appendLanguageToLanguageTag(canonical, langtag, langtagCapacity, strict, status);
2401 11 : reslen += _appendScriptToLanguageTag(canonical, langtag + reslen, langtagCapacity - reslen, strict, status);
2402 11 : reslen += _appendRegionToLanguageTag(canonical, langtag + reslen, langtagCapacity - reslen, strict, status);
2403 11 : reslen += _appendVariantsToLanguageTag(canonical, langtag + reslen, langtagCapacity - reslen, strict, &hadPosix, status);
2404 11 : reslen += _appendKeywordsToLanguageTag(canonical, langtag + reslen, langtagCapacity - reslen, strict, hadPosix, status);
2405 11 : reslen += _appendPrivateuseToLanguageTag(canonical, langtag + reslen, langtagCapacity - reslen, strict, hadPosix, status);
2406 :
2407 11 : return reslen;
2408 : }
2409 :
2410 :
2411 : U_CAPI int32_t U_EXPORT2
2412 0 : uloc_forLanguageTag(const char* langtag,
2413 : char* localeID,
2414 : int32_t localeIDCapacity,
2415 : int32_t* parsedLength,
2416 : UErrorCode* status) {
2417 : ULanguageTag *lt;
2418 0 : int32_t reslen = 0;
2419 : const char *subtag, *p;
2420 : int32_t len;
2421 : int32_t i, n;
2422 0 : UBool noRegion = TRUE;
2423 :
2424 0 : lt = ultag_parse(langtag, -1, parsedLength, status);
2425 0 : if (U_FAILURE(*status)) {
2426 0 : return 0;
2427 : }
2428 :
2429 : /* language */
2430 0 : subtag = ultag_getExtlangSize(lt) > 0 ? ultag_getExtlang(lt, 0) : ultag_getLanguage(lt);
2431 0 : if (uprv_compareInvCharsAsAscii(subtag, LANG_UND) != 0) {
2432 0 : len = (int32_t)uprv_strlen(subtag);
2433 0 : if (len > 0) {
2434 0 : if (reslen < localeIDCapacity) {
2435 0 : uprv_memcpy(localeID, subtag, uprv_min(len, localeIDCapacity - reslen));
2436 : }
2437 0 : reslen += len;
2438 : }
2439 : }
2440 :
2441 : /* script */
2442 0 : subtag = ultag_getScript(lt);
2443 0 : len = (int32_t)uprv_strlen(subtag);
2444 0 : if (len > 0) {
2445 0 : if (reslen < localeIDCapacity) {
2446 0 : *(localeID + reslen) = LOCALE_SEP;
2447 : }
2448 0 : reslen++;
2449 :
2450 : /* write out the script in title case */
2451 0 : p = subtag;
2452 0 : while (*p) {
2453 0 : if (reslen < localeIDCapacity) {
2454 0 : if (p == subtag) {
2455 0 : *(localeID + reslen) = uprv_toupper(*p);
2456 : } else {
2457 0 : *(localeID + reslen) = *p;
2458 : }
2459 : }
2460 0 : reslen++;
2461 0 : p++;
2462 : }
2463 : }
2464 :
2465 : /* region */
2466 0 : subtag = ultag_getRegion(lt);
2467 0 : len = (int32_t)uprv_strlen(subtag);
2468 0 : if (len > 0) {
2469 0 : if (reslen < localeIDCapacity) {
2470 0 : *(localeID + reslen) = LOCALE_SEP;
2471 : }
2472 0 : reslen++;
2473 : /* write out the retion in upper case */
2474 0 : p = subtag;
2475 0 : while (*p) {
2476 0 : if (reslen < localeIDCapacity) {
2477 0 : *(localeID + reslen) = uprv_toupper(*p);
2478 : }
2479 0 : reslen++;
2480 0 : p++;
2481 : }
2482 0 : noRegion = FALSE;
2483 : }
2484 :
2485 : /* variants */
2486 0 : n = ultag_getVariantsSize(lt);
2487 0 : if (n > 0) {
2488 0 : if (noRegion) {
2489 0 : if (reslen < localeIDCapacity) {
2490 0 : *(localeID + reslen) = LOCALE_SEP;
2491 : }
2492 0 : reslen++;
2493 : }
2494 :
2495 0 : for (i = 0; i < n; i++) {
2496 0 : subtag = ultag_getVariant(lt, i);
2497 0 : if (reslen < localeIDCapacity) {
2498 0 : *(localeID + reslen) = LOCALE_SEP;
2499 : }
2500 0 : reslen++;
2501 : /* write out the variant in upper case */
2502 0 : p = subtag;
2503 0 : while (*p) {
2504 0 : if (reslen < localeIDCapacity) {
2505 0 : *(localeID + reslen) = uprv_toupper(*p);
2506 : }
2507 0 : reslen++;
2508 0 : p++;
2509 : }
2510 : }
2511 : }
2512 :
2513 : /* keywords */
2514 0 : n = ultag_getExtensionsSize(lt);
2515 0 : subtag = ultag_getPrivateUse(lt);
2516 0 : if (n > 0 || uprv_strlen(subtag) > 0) {
2517 0 : if (reslen == 0 && n > 0) {
2518 : /* need a language */
2519 0 : if (reslen < localeIDCapacity) {
2520 0 : uprv_memcpy(localeID + reslen, LANG_UND, uprv_min(LANG_UND_LEN, localeIDCapacity - reslen));
2521 : }
2522 0 : reslen += LANG_UND_LEN;
2523 : }
2524 0 : len = _appendKeywords(lt, localeID + reslen, localeIDCapacity - reslen, status);
2525 0 : reslen += len;
2526 : }
2527 :
2528 0 : ultag_close(lt);
2529 0 : return u_terminateChars(localeID, localeIDCapacity, reslen, status);
2530 : }
2531 :
2532 :
|