This source file includes following definitions.
- setlocale_mtsafe
- category_to_name
- search
- setlocale_unixlike
- setlocale_unixlike
- setlocale_single
- langcmp
- get_main_locale_with_same_language
- terrcmp
- get_main_locale_with_same_territory
- setlocale_improved
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 #include <config.h>
20
21
22
23
24
25
26
27
28
29
30 #include <locale.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "localename.h"
37
38 #if HAVE_CFLOCALECOPYPREFERREDLANGUAGES || HAVE_CFPREFERENCESCOPYAPPVALUE
39 # if HAVE_CFLOCALECOPYPREFERREDLANGUAGES
40 # include <CoreFoundation/CFLocale.h>
41 # elif HAVE_CFPREFERENCESCOPYAPPVALUE
42 # include <CoreFoundation/CFPreferences.h>
43 # endif
44 # include <CoreFoundation/CFPropertyList.h>
45 # include <CoreFoundation/CFArray.h>
46 # include <CoreFoundation/CFString.h>
47 extern void gl_locale_name_canonicalize (char *name);
48 #endif
49
50 #if 1
51
52 # undef setlocale
53
54
55 # if NEED_SETLOCALE_IMPROVED
56 # define setlocale_improved rpl_setlocale
57 # elif NEED_SETLOCALE_MTSAFE
58 # define setlocale_mtsafe rpl_setlocale
59 # else
60 # error "This file should only be compiled if NEED_SETLOCALE_IMPROVED || NEED_SETLOCALE_MTSAFE."
61 # endif
62
63
64 # if !SETLOCALE_NULL_ALL_MTSAFE || !SETLOCALE_NULL_ONE_MTSAFE
65
66 # if NEED_SETLOCALE_IMPROVED
67 static
68 # endif
69 char *
70 setlocale_mtsafe (int category, const char *locale)
71 {
72 if (locale == NULL)
73 return (char *) setlocale_null (category);
74 else
75 return setlocale (category, locale);
76 }
77 # else
78
79 # define setlocale_mtsafe setlocale
80
81 # endif
82
83 # if NEED_SETLOCALE_IMPROVED
84
85
86 static const char *
87 category_to_name (int category)
88 {
89 const char *retval;
90
91 switch (category)
92 {
93 case LC_COLLATE:
94 retval = "LC_COLLATE";
95 break;
96 case LC_CTYPE:
97 retval = "LC_CTYPE";
98 break;
99 case LC_MONETARY:
100 retval = "LC_MONETARY";
101 break;
102 case LC_NUMERIC:
103 retval = "LC_NUMERIC";
104 break;
105 case LC_TIME:
106 retval = "LC_TIME";
107 break;
108 case LC_MESSAGES:
109 retval = "LC_MESSAGES";
110 break;
111 default:
112
113 retval = "LC_XXX";
114 }
115
116 return retval;
117 }
118
119 # if defined _WIN32 && ! defined __CYGWIN__
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285 struct table_entry
286 {
287 const char *code;
288 const char *english;
289 };
290 static const struct table_entry language_table[] =
291 {
292 { "af", "Afrikaans" },
293 { "am", "Amharic" },
294 { "ar", "Arabic" },
295 { "arn", "Mapudungun" },
296 { "as", "Assamese" },
297 { "az@cyrillic", "Azeri (Cyrillic)" },
298 { "az@latin", "Azeri (Latin)" },
299 { "ba", "Bashkir" },
300 { "be", "Belarusian" },
301 { "ber", "Tamazight" },
302 { "ber@arabic", "Tamazight (Arabic)" },
303 { "ber@latin", "Tamazight (Latin)" },
304 { "bg", "Bulgarian" },
305 { "bin", "Edo" },
306 { "bn", "Bengali" },
307 { "bn_BD", "Bengali (Bangladesh)" },
308 { "bn_IN", "Bengali (India)" },
309 { "bnt", "Sutu" },
310 { "bo", "Tibetan" },
311 { "br", "Breton" },
312 { "bs", "BSB" },
313 { "bs@cyrillic", "BSC" },
314 { "ca", "Catalan" },
315 { "chr", "Cherokee" },
316 { "co", "Corsican" },
317 { "cpe", "Hawaiian" },
318 { "cs", "Czech" },
319 { "cy", "Welsh" },
320 { "da", "Danish" },
321 { "de", "German" },
322 { "dsb", "Lower Sorbian" },
323 { "dv", "Divehi" },
324 { "el", "Greek" },
325 { "en", "English" },
326 { "es", "Spanish" },
327 { "et", "Estonian" },
328 { "eu", "Basque" },
329 { "fa", "Farsi" },
330 { "ff", "Fulfulde" },
331 { "fi", "Finnish" },
332 { "fo", "Faroese" },
333 { "fr", "French" },
334 { "fy", "Frisian" },
335 { "ga", "IRE" },
336 { "gd", "Gaelic (Scotland)" },
337 { "gd", "Scottish Gaelic" },
338 { "gl", "Galician" },
339 { "gn", "Guarani" },
340 { "gsw", "Alsatian" },
341 { "gu", "Gujarati" },
342 { "ha", "Hausa" },
343 { "he", "Hebrew" },
344 { "hi", "Hindi" },
345 { "hr", "Croatian" },
346 { "hsb", "Upper Sorbian" },
347 { "hu", "Hungarian" },
348 { "hy", "Armenian" },
349 { "id", "Indonesian" },
350 { "ig", "Igbo" },
351 { "ii", "Yi" },
352 { "is", "Icelandic" },
353 { "it", "Italian" },
354 { "iu", "IUK" },
355 { "ja", "Japanese" },
356 { "ka", "Georgian" },
357 { "kk", "Kazakh" },
358 { "kl", "Greenlandic" },
359 { "km", "Cambodian" },
360 { "km", "Khmer" },
361 { "kn", "Kannada" },
362 { "ko", "Korean" },
363 { "kok", "Konkani" },
364 { "kr", "Kanuri" },
365 { "ks", "Kashmiri" },
366 { "ks_IN", "Kashmiri_India" },
367 { "ks_PK", "Kashmiri (Arabic)_Pakistan" },
368 { "ky", "Kyrgyz" },
369 { "la", "Latin" },
370 { "lb", "Luxembourgish" },
371 { "lo", "Lao" },
372 { "lt", "Lithuanian" },
373 { "lv", "Latvian" },
374 { "mi", "Maori" },
375 { "mk", "FYRO Macedonian" },
376 { "mk", "Macedonian" },
377 { "ml", "Malayalam" },
378 { "mn", "Mongolian" },
379 { "mni", "Manipuri" },
380 { "moh", "Mohawk" },
381 { "mr", "Marathi" },
382 { "ms", "Malay" },
383 { "mt", "Maltese" },
384 { "my", "Burmese" },
385 { "nb", "NOR" },
386 { "ne", "Nepali" },
387 { "nic", "Ibibio" },
388 { "nl", "Dutch" },
389 { "nn", "NON" },
390 { "no", "Norwegian" },
391 { "nso", "Northern Sotho" },
392 { "nso", "Sepedi" },
393 { "oc", "Occitan" },
394 { "om", "Oromo" },
395 { "or", "Oriya" },
396 { "pa", "Punjabi" },
397 { "pap", "Papiamentu" },
398 { "pl", "Polish" },
399 { "prs", "Dari" },
400 { "ps", "Pashto" },
401 { "pt", "Portuguese" },
402 { "qu", "Quechua" },
403 { "qut", "K'iche'" },
404 { "rm", "Romansh" },
405 { "ro", "Romanian" },
406 { "ru", "Russian" },
407 { "rw", "Kinyarwanda" },
408 { "sa", "Sanskrit" },
409 { "sah", "Yakut" },
410 { "sd", "Sindhi" },
411 { "se", "Sami (Northern)" },
412 { "se", "Northern Sami" },
413 { "si", "Sinhalese" },
414 { "sk", "Slovak" },
415 { "sl", "Slovenian" },
416 { "sma", "Sami (Southern)" },
417 { "sma", "Southern Sami" },
418 { "smj", "Sami (Lule)" },
419 { "smj", "Lule Sami" },
420 { "smn", "Sami (Inari)" },
421 { "smn", "Inari Sami" },
422 { "sms", "Sami (Skolt)" },
423 { "sms", "Skolt Sami" },
424 { "so", "Somali" },
425 { "sq", "Albanian" },
426 { "sr", "Serbian (Latin)" },
427 { "sr@cyrillic", "SRB" },
428 { "sv", "Swedish" },
429 { "sw", "Swahili" },
430 { "syr", "Syriac" },
431 { "ta", "Tamil" },
432 { "te", "Telugu" },
433 { "tg", "Tajik" },
434 { "th", "Thai" },
435 { "ti", "Tigrinya" },
436 { "tk", "Turkmen" },
437 { "tl", "Filipino" },
438 { "tn", "Tswana" },
439 { "tr", "Turkish" },
440 { "ts", "Tsonga" },
441 { "tt", "Tatar" },
442 { "ug", "Uighur" },
443 { "uk", "Ukrainian" },
444 { "ur", "Urdu" },
445 { "uz", "Uzbek" },
446 { "uz", "Uzbek (Latin)" },
447 { "uz@cyrillic", "Uzbek (Cyrillic)" },
448 { "ve", "Venda" },
449 { "vi", "Vietnamese" },
450 { "wen", "Sorbian" },
451 { "wo", "Wolof" },
452 { "xh", "Xhosa" },
453 { "yi", "Yiddish" },
454 { "yo", "Yoruba" },
455 { "zh", "Chinese" },
456 { "zu", "Zulu" }
457 };
458
459
460
461
462 static const struct table_entry country_table[] =
463 {
464 { "AE", "U.A.E." },
465 { "AF", "Afghanistan" },
466 { "AL", "Albania" },
467 { "AM", "Armenia" },
468 { "AN", "Netherlands Antilles" },
469 { "AR", "Argentina" },
470 { "AT", "Austria" },
471 { "AU", "Australia" },
472 { "AZ", "Azerbaijan" },
473 { "BA", "Bosnia and Herzegovina" },
474 { "BD", "Bangladesh" },
475 { "BE", "Belgium" },
476 { "BG", "Bulgaria" },
477 { "BH", "Bahrain" },
478 { "BN", "Brunei Darussalam" },
479 { "BO", "Bolivia" },
480 { "BR", "Brazil" },
481 { "BT", "Bhutan" },
482 { "BY", "Belarus" },
483 { "BZ", "Belize" },
484 { "CA", "Canada" },
485 { "CG", "Congo" },
486 { "CH", "Switzerland" },
487 { "CI", "Cote d'Ivoire" },
488 { "CL", "Chile" },
489 { "CM", "Cameroon" },
490 { "CN", "People's Republic of China" },
491 { "CO", "Colombia" },
492 { "CR", "Costa Rica" },
493 { "CS", "Serbia and Montenegro" },
494 { "CZ", "Czech Republic" },
495 { "DE", "Germany" },
496 { "DK", "Denmark" },
497 { "DO", "Dominican Republic" },
498 { "DZ", "Algeria" },
499 { "EC", "Ecuador" },
500 { "EE", "Estonia" },
501 { "EG", "Egypt" },
502 { "ER", "Eritrea" },
503 { "ES", "Spain" },
504 { "ET", "Ethiopia" },
505 { "FI", "Finland" },
506 { "FO", "Faroe Islands" },
507 { "FR", "France" },
508 { "GB", "United Kingdom" },
509 { "GD", "Caribbean" },
510 { "GE", "Georgia" },
511 { "GL", "Greenland" },
512 { "GR", "Greece" },
513 { "GT", "Guatemala" },
514 { "HK", "Hong Kong" },
515 { "HK", "Hong Kong S.A.R." },
516 { "HN", "Honduras" },
517 { "HR", "Croatia" },
518 { "HT", "Haiti" },
519 { "HU", "Hungary" },
520 { "ID", "Indonesia" },
521 { "IE", "Ireland" },
522 { "IL", "Israel" },
523 { "IN", "India" },
524 { "IQ", "Iraq" },
525 { "IR", "Iran" },
526 { "IS", "Iceland" },
527 { "IT", "Italy" },
528 { "JM", "Jamaica" },
529 { "JO", "Jordan" },
530 { "JP", "Japan" },
531 { "KE", "Kenya" },
532 { "KG", "Kyrgyzstan" },
533 { "KH", "Cambodia" },
534 { "KR", "South Korea" },
535 { "KW", "Kuwait" },
536 { "KZ", "Kazakhstan" },
537 { "LA", "Laos" },
538 { "LB", "Lebanon" },
539 { "LI", "Liechtenstein" },
540 { "LK", "Sri Lanka" },
541 { "LT", "Lithuania" },
542 { "LU", "Luxembourg" },
543 { "LV", "Latvia" },
544 { "LY", "Libya" },
545 { "MA", "Morocco" },
546 { "MC", "Principality of Monaco" },
547 { "MD", "Moldava" },
548 { "MD", "Moldova" },
549 { "ME", "Montenegro" },
550 { "MK", "Former Yugoslav Republic of Macedonia" },
551 { "ML", "Mali" },
552 { "MM", "Myanmar" },
553 { "MN", "Mongolia" },
554 { "MO", "Macau S.A.R." },
555 { "MT", "Malta" },
556 { "MV", "Maldives" },
557 { "MX", "Mexico" },
558 { "MY", "Malaysia" },
559 { "NG", "Nigeria" },
560 { "NI", "Nicaragua" },
561 { "NL", "Netherlands" },
562 { "NO", "Norway" },
563 { "NP", "Nepal" },
564 { "NZ", "New Zealand" },
565 { "OM", "Oman" },
566 { "PA", "Panama" },
567 { "PE", "Peru" },
568 { "PH", "Philippines" },
569 { "PK", "Islamic Republic of Pakistan" },
570 { "PL", "Poland" },
571 { "PR", "Puerto Rico" },
572 { "PT", "Portugal" },
573 { "PY", "Paraguay" },
574 { "QA", "Qatar" },
575 { "RE", "Reunion" },
576 { "RO", "Romania" },
577 { "RS", "Serbia" },
578 { "RU", "Russia" },
579 { "RW", "Rwanda" },
580 { "SA", "Saudi Arabia" },
581 { "SE", "Sweden" },
582 { "SG", "Singapore" },
583 { "SI", "Slovenia" },
584 { "SK", "Slovak" },
585 { "SN", "Senegal" },
586 { "SO", "Somalia" },
587 { "SR", "Suriname" },
588 { "SV", "El Salvador" },
589 { "SY", "Syria" },
590 { "TH", "Thailand" },
591 { "TJ", "Tajikistan" },
592 { "TM", "Turkmenistan" },
593 { "TN", "Tunisia" },
594 { "TR", "Turkey" },
595 { "TT", "Trinidad and Tobago" },
596 { "TW", "Taiwan" },
597 { "TZ", "Tanzania" },
598 { "UA", "Ukraine" },
599 { "US", "United States" },
600 { "UY", "Uruguay" },
601 { "VA", "Vatican" },
602 { "VE", "Venezuela" },
603 { "VN", "Viet Nam" },
604 { "YE", "Yemen" },
605 { "ZA", "South Africa" },
606 { "ZW", "Zimbabwe" }
607 };
608
609
610
611 typedef struct { size_t lo; size_t hi; } range_t;
612 static void
613 search (const struct table_entry *table, size_t table_size, const char *string,
614 range_t *result)
615 {
616
617 size_t hi = table_size;
618 size_t lo = 0;
619 while (lo < hi)
620 {
621
622
623
624 size_t mid = (hi + lo) >> 1;
625 int cmp = strcmp (table[mid].code, string);
626 if (cmp < 0)
627 lo = mid + 1;
628 else if (cmp > 0)
629 hi = mid;
630 else
631 {
632
633
634
635 {
636 size_t i;
637
638 for (i = mid; i > lo; )
639 {
640 i--;
641 if (strcmp (table[i].code, string) < 0)
642 {
643 lo = i + 1;
644 break;
645 }
646 }
647 }
648 {
649 size_t i;
650
651 for (i = mid + 1; i < hi; i++)
652 {
653 if (strcmp (table[i].code, string) > 0)
654 {
655 hi = i;
656 break;
657 }
658 }
659 }
660
661
662
663 break;
664 }
665 }
666 result->lo = lo;
667 result->hi = hi;
668 }
669
670
671
672 static char *
673 setlocale_unixlike (int category, const char *locale)
674 {
675 char *result;
676 char llCC_buf[64];
677 char ll_buf[64];
678 char CC_buf[64];
679
680
681
682 if (locale != NULL && strcmp (locale, "POSIX") == 0)
683 locale = "C";
684
685
686 result = setlocale_mtsafe (category, locale);
687 if (result != NULL)
688 return result;
689
690
691
692
693 if (strlen (locale) < sizeof (llCC_buf))
694 {
695
696 {
697 const char *p = locale;
698 char *q = llCC_buf;
699
700
701 for (; *p != '\0' && *p != '.'; p++, q++)
702 *q = *p;
703 if (*p == '.')
704
705 for (; *p != '\0' && *p != '@'; p++)
706 ;
707
708 for (; *p != '\0'; p++, q++)
709 *q = *p;
710 *q = '\0';
711 }
712
713
714
715 if (strcmp (llCC_buf, locale) != 0)
716 {
717 result = setlocale (category, llCC_buf);
718 if (result != NULL)
719 return result;
720 }
721
722 {
723 range_t range;
724 size_t i;
725
726 search (language_table,
727 sizeof (language_table) / sizeof (language_table[0]),
728 llCC_buf,
729 &range);
730
731 for (i = range.lo; i < range.hi; i++)
732 {
733
734 result = setlocale (category, language_table[i].english);
735 if (result != NULL)
736 return result;
737 }
738 }
739
740
741
742
743 {
744 const char *underscore = strchr (llCC_buf, '_');
745 if (underscore != NULL)
746 {
747 const char *territory_start = underscore + 1;
748 const char *territory_end = strchr (territory_start, '@');
749 if (territory_end == NULL)
750 territory_end = territory_start + strlen (territory_start);
751
752 memcpy (ll_buf, llCC_buf, underscore - llCC_buf);
753 strcpy (ll_buf + (underscore - llCC_buf), territory_end);
754
755 memcpy (CC_buf, territory_start, territory_end - territory_start);
756 CC_buf[territory_end - territory_start] = '\0';
757
758 {
759
760
761 range_t language_range;
762
763 search (language_table,
764 sizeof (language_table) / sizeof (language_table[0]),
765 ll_buf,
766 &language_range);
767 if (language_range.lo < language_range.hi)
768 {
769 range_t country_range;
770
771 search (country_table,
772 sizeof (country_table) / sizeof (country_table[0]),
773 CC_buf,
774 &country_range);
775 if (country_range.lo < country_range.hi)
776 {
777 size_t i;
778 size_t j;
779
780 for (i = language_range.lo; i < language_range.hi; i++)
781 for (j = country_range.lo; j < country_range.hi; j++)
782 {
783
784 const char *part1 = language_table[i].english;
785 size_t part1_len = strlen (part1);
786 const char *part2 = country_table[j].english;
787 size_t part2_len = strlen (part2) + 1;
788 char buf[64+64];
789
790 if (!(part1_len + 1 + part2_len <= sizeof (buf)))
791 abort ();
792 memcpy (buf, part1, part1_len);
793 buf[part1_len] = '_';
794 memcpy (buf + part1_len + 1, part2, part2_len);
795
796
797 result = setlocale (category, buf);
798 if (result != NULL)
799 return result;
800 }
801 }
802
803
804
805
806 {
807 size_t i;
808
809 for (i = language_range.lo; i < language_range.hi; i++)
810 {
811
812 result =
813 setlocale (category, language_table[i].english);
814 if (result != NULL)
815 return result;
816 }
817 }
818 }
819 }
820 }
821 }
822 }
823
824
825 return NULL;
826 }
827
828 # elif defined __ANDROID__
829
830
831 static char *
832 setlocale_unixlike (int category, const char *locale)
833 {
834 char *result = setlocale_mtsafe (category, locale);
835 if (result == NULL)
836 switch (category)
837 {
838 case LC_CTYPE:
839 case LC_NUMERIC:
840 case LC_TIME:
841 case LC_COLLATE:
842 case LC_MONETARY:
843 case LC_MESSAGES:
844 case LC_ALL:
845 case LC_PAPER:
846 case LC_NAME:
847 case LC_ADDRESS:
848 case LC_TELEPHONE:
849 case LC_MEASUREMENT:
850 if (locale == NULL
851 || strcmp (locale, "C") == 0 || strcmp (locale, "POSIX") == 0)
852 result = (char *) "C";
853 break;
854 default:
855 break;
856 }
857 return result;
858 }
859 # define setlocale setlocale_unixlike
860
861 # else
862 # define setlocale_unixlike setlocale_mtsafe
863 # endif
864
865 # if LC_MESSAGES == 1729
866
867
868 static char lc_messages_name[64] = "C";
869
870
871 static char *
872 setlocale_single (int category, const char *locale)
873 {
874 if (category == LC_MESSAGES)
875 {
876 if (locale != NULL)
877 {
878 lc_messages_name[sizeof (lc_messages_name) - 1] = '\0';
879 strncpy (lc_messages_name, locale, sizeof (lc_messages_name) - 1);
880 }
881 return lc_messages_name;
882 }
883 else
884 return setlocale_unixlike (category, locale);
885 }
886
887 # else
888 # define setlocale_single setlocale_unixlike
889 # endif
890
891 # if defined __APPLE__ && defined __MACH__
892
893
894 static char const locales_with_principal_territory[][6 + 1] =
895 {
896
897 "ace_ID",
898 "af_ZA",
899 "ak_GH",
900 "am_ET",
901 "an_ES",
902 "ang_GB",
903 "arn_CL",
904 "as_IN",
905 "ast_ES",
906 "av_RU",
907 "awa_IN",
908 "az_AZ",
909 "ban_ID",
910 "be_BY",
911 "bej_SD",
912 "bem_ZM",
913 "bg_BG",
914 "bho_IN",
915 "bi_VU",
916 "bik_PH",
917 "bin_NG",
918 "bm_ML",
919 "bn_IN",
920 "bo_CN",
921 "br_FR",
922 "bs_BA",
923 "bug_ID",
924 "ca_ES",
925 "ce_RU",
926 "ceb_PH",
927 "co_FR",
928 "cr_CA",
929
930
931 "cs_CZ",
932 "csb_PL",
933 "cy_GB",
934 "da_DK",
935 "de_DE",
936 "din_SD",
937 "doi_IN",
938 "dsb_DE",
939 "dv_MV",
940 "dz_BT",
941 "ee_GH",
942 "el_GR",
943
944
945 "es_ES",
946 "et_EE",
947 "fa_IR",
948 "fi_FI",
949 "fil_PH",
950 "fj_FJ",
951 "fo_FO",
952 "fon_BJ",
953 "fr_FR",
954 "fur_IT",
955 "fy_NL",
956 "ga_IE",
957 "gd_GB",
958 "gon_IN",
959 "gsw_CH",
960 "gu_IN",
961 "he_IL",
962 "hi_IN",
963 "hil_PH",
964 "hr_HR",
965 "hsb_DE",
966 "ht_HT",
967 "hu_HU",
968 "hy_AM",
969 "id_ID",
970 "ig_NG",
971 "ii_CN",
972 "ilo_PH",
973 "is_IS",
974 "it_IT",
975 "ja_JP",
976 "jab_NG",
977 "jv_ID",
978 "ka_GE",
979 "kab_DZ",
980 "kaj_NG",
981 "kam_KE",
982 "kmb_AO",
983 "kcg_NG",
984 "kdm_NG",
985 "kg_CD",
986 "kk_KZ",
987 "kl_GL",
988 "km_KH",
989 "kn_IN",
990 "ko_KR",
991 "kok_IN",
992 "kr_NG",
993 "kru_IN",
994 "ky_KG",
995 "lg_UG",
996 "li_BE",
997 "lo_LA",
998 "lt_LT",
999 "lu_CD",
1000 "lua_CD",
1001 "luo_KE",
1002 "lv_LV",
1003 "mad_ID",
1004 "mag_IN",
1005 "mai_IN",
1006 "mak_ID",
1007 "man_ML",
1008 "men_SL",
1009 "mfe_MU",
1010 "mg_MG",
1011 "mi_NZ",
1012 "min_ID",
1013 "mk_MK",
1014 "ml_IN",
1015 "mn_MN",
1016 "mni_IN",
1017 "mos_BF",
1018 "mr_IN",
1019 "ms_MY",
1020 "mt_MT",
1021 "mwr_IN",
1022 "my_MM",
1023 "na_NR",
1024 "nah_MX",
1025 "nap_IT",
1026 "nb_NO",
1027 "nds_DE",
1028 "ne_NP",
1029 "nl_NL",
1030 "nn_NO",
1031 "no_NO",
1032 "nr_ZA",
1033 "nso_ZA",
1034 "ny_MW",
1035 "nym_TZ",
1036 "nyn_UG",
1037 "oc_FR",
1038 "oj_CA",
1039 "or_IN",
1040 "pa_IN",
1041 "pag_PH",
1042 "pam_PH",
1043 "pap_AN",
1044 "pbb_CO",
1045 "pl_PL",
1046 "ps_AF",
1047 "pt_PT",
1048 "raj_IN",
1049 "rm_CH",
1050 "rn_BI",
1051 "ro_RO",
1052 "ru_RU",
1053 "rw_RW",
1054 "sa_IN",
1055 "sah_RU",
1056 "sas_ID",
1057 "sat_IN",
1058 "sc_IT",
1059 "scn_IT",
1060 "sg_CF",
1061 "shn_MM",
1062 "si_LK",
1063 "sid_ET",
1064 "sk_SK",
1065 "sl_SI",
1066 "sm_WS",
1067 "smn_FI",
1068 "sms_FI",
1069 "so_SO",
1070 "sq_AL",
1071 "sr_RS",
1072 "srr_SN",
1073 "suk_TZ",
1074 "sus_GN",
1075 "sv_SE",
1076 "te_IN",
1077 "tem_SL",
1078 "tet_ID",
1079 "tg_TJ",
1080 "th_TH",
1081 "ti_ER",
1082 "tiv_NG",
1083 "tk_TM",
1084 "tl_PH",
1085 "to_TO",
1086 "tpi_PG",
1087 "tr_TR",
1088 "tum_MW",
1089 "ug_CN",
1090 "uk_UA",
1091 "umb_AO",
1092 "ur_PK",
1093 "uz_UZ",
1094 "ve_ZA",
1095 "vi_VN",
1096 "wa_BE",
1097 "wal_ET",
1098 "war_PH",
1099 "wen_DE",
1100 "yao_MW",
1101 "zap_MX"
1102 };
1103
1104
1105 static int
1106 langcmp (const char *locale1, const char *locale2)
1107 {
1108 size_t locale1_len;
1109 size_t locale2_len;
1110 int cmp;
1111
1112 {
1113 const char *locale1_end = strchr (locale1, '_');
1114 if (locale1_end != NULL)
1115 locale1_len = locale1_end - locale1;
1116 else
1117 locale1_len = strlen (locale1);
1118 }
1119 {
1120 const char *locale2_end = strchr (locale2, '_');
1121 if (locale2_end != NULL)
1122 locale2_len = locale2_end - locale2;
1123 else
1124 locale2_len = strlen (locale2);
1125 }
1126
1127 if (locale1_len < locale2_len)
1128 {
1129 cmp = memcmp (locale1, locale2, locale1_len);
1130 if (cmp == 0)
1131 cmp = -1;
1132 }
1133 else
1134 {
1135 cmp = memcmp (locale1, locale2, locale2_len);
1136 if (locale1_len > locale2_len && cmp == 0)
1137 cmp = 1;
1138 }
1139
1140 return cmp;
1141 }
1142
1143
1144
1145
1146 static const char *
1147 get_main_locale_with_same_language (const char *locale)
1148 {
1149 # define table locales_with_principal_territory
1150
1151 size_t hi = sizeof (table) / sizeof (table[0]);
1152 size_t lo = 0;
1153 while (lo < hi)
1154 {
1155
1156
1157
1158 size_t mid = (hi + lo) >> 1;
1159 int cmp = langcmp (table[mid], locale);
1160 if (cmp < 0)
1161 lo = mid + 1;
1162 else if (cmp > 0)
1163 hi = mid;
1164 else
1165 {
1166
1167
1168
1169 if (mid > lo && langcmp (table[mid - 1], locale) >= 0)
1170 abort ();
1171 if (mid + 1 < hi && langcmp (table[mid + 1], locale) <= 0)
1172 abort ();
1173 return table[mid];
1174 }
1175 }
1176 # undef table
1177 return NULL;
1178 }
1179
1180
1181 static char const locales_with_principal_language[][6 + 1] =
1182 {
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199 "ca_AD",
1200 "ar_AE",
1201 "ps_AF",
1202 "en_AG",
1203 "sq_AL",
1204 "hy_AM",
1205 "pap_AN",
1206 "pt_AO",
1207 "es_AR",
1208 "de_AT",
1209 "en_AU",
1210
1211 "az_AZ",
1212 "bs_BA",
1213 "bn_BD",
1214 "nl_BE",
1215 "fr_BF",
1216 "bg_BG",
1217 "ar_BH",
1218 "rn_BI",
1219 "fr_BJ",
1220 "es_BO",
1221 "pt_BR",
1222 "dz_BT",
1223 "en_BW",
1224 "be_BY",
1225 "en_CA",
1226 "fr_CD",
1227 "sg_CF",
1228 "de_CH",
1229 "es_CL",
1230 "zh_CN",
1231 "es_CO",
1232 "es_CR",
1233 "es_CU",
1234
1235 "el_CY",
1236 "cs_CZ",
1237 "de_DE",
1238
1239 "da_DK",
1240 "es_DO",
1241 "ar_DZ",
1242 "es_EC",
1243 "et_EE",
1244 "ar_EG",
1245 "ti_ER",
1246 "es_ES",
1247 "am_ET",
1248 "fi_FI",
1249
1250 "fo_FO",
1251 "fr_FR",
1252 "en_GB",
1253 "ka_GE",
1254 "en_GH",
1255 "kl_GL",
1256 "fr_GN",
1257 "el_GR",
1258 "es_GT",
1259 "zh_HK",
1260 "es_HN",
1261 "hr_HR",
1262 "ht_HT",
1263 "hu_HU",
1264 "id_ID",
1265 "en_IE",
1266 "he_IL",
1267 "hi_IN",
1268 "ar_IQ",
1269 "fa_IR",
1270 "is_IS",
1271 "it_IT",
1272 "ar_JO",
1273 "ja_JP",
1274 "sw_KE",
1275 "ky_KG",
1276 "km_KH",
1277 "ko_KR",
1278 "ar_KW",
1279 "kk_KZ",
1280 "lo_LA",
1281 "ar_LB",
1282 "de_LI",
1283 "si_LK",
1284 "lt_LT",
1285
1286 "lv_LV",
1287 "ar_LY",
1288 "ar_MA",
1289 "sr_ME",
1290 "mg_MG",
1291 "mk_MK",
1292 "fr_ML",
1293 "my_MM",
1294 "mn_MN",
1295 "mt_MT",
1296 "mfe_MU",
1297 "dv_MV",
1298 "ny_MW",
1299 "es_MX",
1300 "ms_MY",
1301 "en_NG",
1302 "es_NI",
1303 "nl_NL",
1304 "no_NO",
1305 "ne_NP",
1306 "na_NR",
1307 "niu_NU",
1308 "en_NZ",
1309 "ar_OM",
1310 "es_PA",
1311 "es_PE",
1312 "tpi_PG",
1313 "fil_PH",
1314 "pa_PK",
1315 "pl_PL",
1316 "es_PR",
1317 "pt_PT",
1318 "es_PY",
1319 "ar_QA",
1320 "ro_RO",
1321 "sr_RS",
1322 "ru_RU",
1323 "rw_RW",
1324 "ar_SA",
1325 "en_SC",
1326 "ar_SD",
1327 "sv_SE",
1328 "en_SG",
1329 "sl_SI",
1330 "sk_SK",
1331 "en_SL",
1332 "fr_SN",
1333 "so_SO",
1334 "ar_SS",
1335 "es_SV",
1336 "ar_SY",
1337 "th_TH",
1338 "tg_TJ",
1339 "tk_TM",
1340 "ar_TN",
1341 "to_TO",
1342 "tr_TR",
1343 "zh_TW",
1344 "sw_TZ",
1345 "uk_UA",
1346 "lg_UG",
1347 "en_US",
1348 "es_UY",
1349 "uz_UZ",
1350 "es_VE",
1351 "vi_VN",
1352 "bi_VU",
1353 "sm_WS",
1354 "ar_YE",
1355 "en_ZA",
1356 "en_ZM",
1357 "en_ZW"
1358 };
1359
1360
1361 static int
1362 terrcmp (const char *locale1, const char *locale2)
1363 {
1364 const char *territory1 = strrchr (locale1, '_') + 1;
1365 const char *territory2 = strrchr (locale2, '_') + 1;
1366
1367 return strcmp (territory1, territory2);
1368 }
1369
1370
1371
1372
1373 static const char *
1374 get_main_locale_with_same_territory (const char *locale)
1375 {
1376 if (strrchr (locale, '_') != NULL)
1377 {
1378 # define table locales_with_principal_language
1379
1380 size_t hi = sizeof (table) / sizeof (table[0]);
1381 size_t lo = 0;
1382 while (lo < hi)
1383 {
1384
1385
1386
1387 size_t mid = (hi + lo) >> 1;
1388 int cmp = terrcmp (table[mid], locale);
1389 if (cmp < 0)
1390 lo = mid + 1;
1391 else if (cmp > 0)
1392 hi = mid;
1393 else
1394 {
1395
1396
1397
1398 if (mid > lo && terrcmp (table[mid - 1], locale) >= 0)
1399 abort ();
1400 if (mid + 1 < hi && terrcmp (table[mid + 1], locale) <= 0)
1401 abort ();
1402 return table[mid];
1403 }
1404 }
1405 # undef table
1406 }
1407 return NULL;
1408 }
1409
1410 # endif
1411
1412 char *
1413 setlocale_improved (int category, const char *locale)
1414 {
1415 if (locale != NULL && locale[0] == '\0')
1416 {
1417
1418 if (category == LC_ALL)
1419 {
1420
1421 static int const categories[] =
1422 {
1423 LC_CTYPE,
1424 LC_NUMERIC,
1425 LC_TIME,
1426 LC_COLLATE,
1427 LC_MONETARY,
1428 LC_MESSAGES
1429 };
1430 char *saved_locale;
1431 const char *base_name;
1432 unsigned int i;
1433
1434
1435 saved_locale = setlocale (LC_ALL, NULL);
1436 if (saved_locale == NULL)
1437 return NULL;
1438 saved_locale = strdup (saved_locale);
1439 if (saved_locale == NULL)
1440 return NULL;
1441
1442
1443
1444
1445 base_name =
1446 gl_locale_name_environ (LC_CTYPE, category_to_name (LC_CTYPE));
1447 if (base_name == NULL)
1448 base_name = gl_locale_name_default ();
1449
1450 if (setlocale_unixlike (LC_ALL, base_name) != NULL)
1451 {
1452
1453 i = 1;
1454 }
1455 else
1456 {
1457
1458
1459
1460 base_name = "C";
1461 if (setlocale_unixlike (LC_ALL, base_name) == NULL)
1462 goto fail;
1463 i = 0;
1464 }
1465 # if defined _WIN32 && ! defined __CYGWIN__
1466
1467
1468
1469 if (strchr (base_name, '.') != NULL
1470 && strcmp (setlocale (LC_CTYPE, NULL), "C") == 0)
1471 goto fail;
1472 # endif
1473
1474 for (; i < sizeof (categories) / sizeof (categories[0]); i++)
1475 {
1476 int cat = categories[i];
1477 const char *name;
1478
1479 name = gl_locale_name_environ (cat, category_to_name (cat));
1480 if (name == NULL)
1481 name = gl_locale_name_default ();
1482
1483
1484
1485 if (strcmp (name, base_name) != 0
1486 # if LC_MESSAGES == 1729
1487 || cat == LC_MESSAGES
1488 # endif
1489 )
1490 if (setlocale_single (cat, name) == NULL)
1491 # if defined __APPLE__ && defined __MACH__
1492 {
1493
1494
1495
1496
1497
1498
1499
1500
1501 int warn = 0;
1502
1503 if (cat == LC_CTYPE)
1504 warn = (setlocale_single (cat, "UTF-8") == NULL);
1505 else if (cat == LC_MESSAGES)
1506 {
1507 # if HAVE_CFLOCALECOPYPREFERREDLANGUAGES || HAVE_CFPREFERENCESCOPYAPPVALUE
1508
1509 # if HAVE_CFLOCALECOPYPREFERREDLANGUAGES
1510 CFArrayRef prefArray = CFLocaleCopyPreferredLanguages ();
1511 # elif HAVE_CFPREFERENCESCOPYAPPVALUE
1512 CFTypeRef preferences =
1513 CFPreferencesCopyAppValue (CFSTR ("AppleLanguages"),
1514 kCFPreferencesCurrentApplication);
1515 if (preferences != NULL
1516 && CFGetTypeID (preferences) == CFArrayGetTypeID ())
1517 {
1518 CFArrayRef prefArray = (CFArrayRef)preferences;
1519 # endif
1520 int n = CFArrayGetCount (prefArray);
1521 if (n > 0)
1522 {
1523 char buf[256];
1524 CFTypeRef element = CFArrayGetValueAtIndex (prefArray, 0);
1525 if (element != NULL
1526 && CFGetTypeID (element) == CFStringGetTypeID ()
1527 && CFStringGetCString ((CFStringRef)element,
1528 buf, sizeof (buf),
1529 kCFStringEncodingASCII))
1530 {
1531
1532
1533 char *last_minus = strrchr (buf, '-');
1534 if (last_minus != NULL)
1535 *last_minus = '\0';
1536
1537
1538
1539 gl_locale_name_canonicalize (buf);
1540
1541
1542 if (setlocale_single (cat, buf) == NULL)
1543 {
1544 const char *last_try =
1545 get_main_locale_with_same_language (buf);
1546
1547 if (last_try == NULL
1548 || setlocale_single (cat, last_try) == NULL)
1549 warn = 1;
1550 }
1551 }
1552 }
1553 # if HAVE_CFLOCALECOPYPREFERREDLANGUAGES
1554 CFRelease (prefArray);
1555 # elif HAVE_CFPREFERENCESCOPYAPPVALUE
1556 }
1557 # endif
1558 # else
1559 const char *last_try =
1560 get_main_locale_with_same_language (name);
1561
1562 if (last_try == NULL
1563 || setlocale_single (cat, last_try) == NULL)
1564 warn = 1;
1565 # endif
1566 }
1567 else
1568 {
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583 const char *last_try =
1584 get_main_locale_with_same_territory (name);
1585
1586 if (last_try == NULL
1587 || setlocale_single (cat, last_try) == NULL)
1588 warn = 1;
1589 }
1590
1591 if (warn)
1592 {
1593
1594
1595
1596
1597 const char *verbose = getenv ("SETLOCALE_VERBOSE");
1598 if (verbose != NULL && verbose[0] != '\0')
1599 fprintf (stderr,
1600 "Warning: Failed to set locale category %s to %s.\n",
1601 category_to_name (cat), name);
1602 }
1603 }
1604 # else
1605 goto fail;
1606 # endif
1607 }
1608
1609
1610 free (saved_locale);
1611 return setlocale (LC_ALL, NULL);
1612
1613 fail:
1614 if (saved_locale[0] != '\0')
1615 setlocale (LC_ALL, saved_locale);
1616 free (saved_locale);
1617 return NULL;
1618 }
1619 else
1620 {
1621 const char *name =
1622 gl_locale_name_environ (category, category_to_name (category));
1623 if (name == NULL)
1624 name = gl_locale_name_default ();
1625
1626 return setlocale_single (category, name);
1627 }
1628 }
1629 else
1630 {
1631 # if defined _WIN32 && ! defined __CYGWIN__
1632 if (category == LC_ALL && locale != NULL && strchr (locale, '.') != NULL)
1633 {
1634 char *saved_locale;
1635
1636
1637 saved_locale = setlocale (LC_ALL, NULL);
1638 if (saved_locale == NULL)
1639 return NULL;
1640 saved_locale = strdup (saved_locale);
1641 if (saved_locale == NULL)
1642 return NULL;
1643
1644 if (setlocale_unixlike (LC_ALL, locale) == NULL)
1645 {
1646 free (saved_locale);
1647 return NULL;
1648 }
1649
1650
1651
1652
1653 if (strcmp (setlocale (LC_CTYPE, NULL), "C") == 0)
1654 {
1655 if (saved_locale[0] != '\0')
1656 setlocale (LC_ALL, saved_locale);
1657 free (saved_locale);
1658 return NULL;
1659 }
1660
1661
1662 free (saved_locale);
1663 return setlocale (LC_ALL, NULL);
1664 }
1665 else
1666 # endif
1667 return setlocale_single (category, locale);
1668 }
1669 }
1670
1671 # endif
1672
1673 #endif