1 /*
2 * Copyright (C) 2014 Akihiro Tsukada
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation version 2.1 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include "libdvbv5/countries.h"
19
20 #include <ctype.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <strings.h>
24
25 struct cCountry {
26 enum dvb_country_t id;
27 const char *alpha2_name;
28 const char *alpha3_name;
29 const char *short_name;
30 };
31
32 /*
33 * constants from ISO 3166-1.
34 * sorted alphabetically by two-letter(alpha-2) country name and
35 * in the same order with enum dvb_country_t.
36 */
37 static const struct cCountry country_list[] = {
38 /* id, alpha-2, alpha-3, short name */
39
40 { COUNTRY_UNKNOWN, "", "", "(Unknown Country)" },
41
42 { AD, "AD", "AND", "Andorra" },
43 { AE, "AE", "ARE", "United Arab Emirates" },
44 { AF, "AF", "AFG", "Afghanistan" },
45 { AG, "AG", "ATG", "Antigua and Barbuda" },
46 { AI, "AI", "AIA", "Anguilla" },
47 { AL, "AL", "ALB", "Albania" },
48 { AM, "AM", "ARM", "Armenia" },
49 { AO, "AO", "AGO", "Angola" },
50 { AQ, "AQ", "ATA", "Antarctica" },
51 { AR, "AR", "ARG", "Argentina" },
52 { AS, "AS", "ASM", "American Samoa" },
53 { AT, "AT", "AUT", "Austria" },
54 { AU, "AU", "AUS", "Australia" },
55 { AW, "AW", "ABW", "Aruba" },
56 { AX, "AX", "ALA", "Aland Islands" },
57 { AZ, "AZ", "AZE", "Azerbaijan" },
58 { BA, "BA", "BIH", "Bosnia and Herzegovina" },
59 { BB, "BB", "BRB", "Barbados" },
60 { BD, "BD", "BGD", "Bangladesh" },
61 { BE, "BE", "BEL", "Belgium" },
62 { BF, "BF", "BFA", "Burkina Faso" },
63 { BG, "BG", "BGR", "Bulgaria" },
64 { BH, "BH", "BHR", "Bahrain" },
65 { BI, "BI", "BDI", "Burundi" },
66 { BJ, "BJ", "BEN", "Benin" },
67 { BL, "BL", "534", "Saint Barthelemy" },
68 { BM, "BM", "BMU", "Bermuda" },
69 { BN, "BN", "BRN", "Brunei Darussalam" },
70 { BO, "BO", "BOL", "Plurinational State of Bolivia" },
71 { BQ, "BQ", "BES", "Bonaire, Saint Eustatius and Saba" },
72 { BR, "BR", "BRA", "Brazil" },
73 { BS, "BS", "BHS", "Bahamas" },
74 { BT, "BT", "BTN", "Bhutan" },
75 { BV, "BV", "BVT", "Bouvet Island" },
76 { BW, "BW", "BWA", "Botswana" },
77 { BY, "BY", "BLR", "Belarus" },
78 { BZ, "BZ", "BLZ", "Belize" },
79 { CA, "CA", "CAN", "Canada" },
80 { CC, "CC", "CCK", "Cocos (Keeling) Islands" },
81 { CD, "CD", "COD", "The Democratic Republic of the Congo" },
82 { CF, "CF", "CAF", "Central African Republic" },
83 { CG, "CG", "COG", "Congo" },
84 { CH, "CH", "CHE", "Switzerland" },
85 { CI, "CI", "CIV", "Cote d'Ivoire" },
86 { CK, "CK", "COK", "Cook Islands" },
87 { CL, "CL", "CHL", "Chile" },
88 { CM, "CM", "CMR", "Cameroon" },
89 { CN, "CN", "CHN", "China" },
90 { CO, "CO", "COL", "Colombia" },
91 { CR, "CR", "CRI", "Costa Rica" },
92 { CU, "CU", "CUB", "Cuba" },
93 { CV, "CV", "CPV", "Cape Verde" },
94 { CW, "CW", "CUW", "Curacao" },
95 { CX, "CX", "CXR", "Christmas Island" },
96 { CY, "CY", "CYP", "Cyprus" },
97 { CZ, "CZ", "CZE", "Czech Republic" },
98 { DE, "DE", "DEU", "Germany" },
99 { DJ, "DJ", "DJI", "Djibouti" },
100 { DK, "DK", "DNK", "Denmark" },
101 { DM, "DM", "DMA", "Dominica" },
102 { DO, "DO", "DOM", "Dominican Republic" },
103 { DZ, "DZ", "DZA", "Algeria" },
104 { EC, "EC", "ECU", "Ecuador" },
105 { EE, "EE", "EST", "Estonia" },
106 { EG, "EG", "EGY", "Egypt" },
107 { EH, "EH", "ESH", "Western Sahara" },
108 { ER, "ER", "ERI", "Eritrea" },
109 { ES, "ES", "ESP", "Spain" },
110 { ET, "ET", "ETH", "Ethiopia" },
111 { FI, "FI", "FIN", "Finland" },
112 { FJ, "FJ", "FJI", "Fiji" },
113 { FK, "FK", "FLK", "Falkland Islands (Malvinas)" },
114 { FM, "FM", "FSM", "Federated States of Micronesia" },
115 { FO, "FO", "534", "Faroe Islands" },
116 { FR, "FR", "FRA", "France" },
117 { GA, "GA", "GAB", "Gabon" },
118 { GB, "GB", "GBR", "United Kingdom" },
119 { GD, "GD", "GRD", "Grenada" },
120 { GE, "GE", "GEO", "Georgia" },
121 { GF, "GF", "GUF", "French Guiana" },
122 { GG, "GG", "GGY", "Guernsey" },
123 { GH, "GH", "GHA", "Ghana" },
124 { GI, "GI", "GIB", "Gibraltar" },
125 { GL, "GL", "GRL", "Greenland" },
126 { GM, "GM", "GMB", "Gambia" },
127 { GN, "GN", "GIN", "Guinea" },
128 { GP, "GP", "GLP", "Guadeloupe" },
129 { GQ, "GQ", "GNQ", "Equatorial Guinea" },
130 { GR, "GR", "GRC", "Greece" },
131 { GS, "GS", "SGS", "South Georgia and the South Sandwich Islands" },
132 { GT, "GT", "GTM", "Guatemala" },
133 { GU, "GU", "GUM", "Guam" },
134 { GW, "GW", "GNB", "Guinea-Bissau" },
135 { GY, "GY", "GUY", "Guyana" },
136 { HK, "HK", "HKG", "Hong Kong" },
137 { HM, "HM", "HMD", "Heard Island and McDonald Islands" },
138 { HN, "HN", "HND", "Honduras" },
139 { HR, "HR", "HRV", "Croatia" },
140 { HT, "HT", "HTI", "Haiti" },
141 { HU, "HU", "HUN", "Hungary" },
142 { ID, "ID", "IDN", "Indonesia" },
143 { IE, "IE", "IRL", "Ireland" },
144 { IL, "IL", "ISR", "Israel" },
145 { IM, "IM", "IMN", "Isle of Man" },
146 { IN, "IN", "IND", "India" },
147 { IO, "IO", "IOT", "British Indian Ocean Territory" },
148 { IQ, "IQ", "IRQ", "Iraq" },
149 { IR, "IR", "IRN", "Islamic Republic of Iran" },
150 { IS, "IS", "ISL", "Iceland" },
151 { IT, "IT", "ITA", "Italy" },
152 { JE, "JE", "JEY", "Jersey" },
153 { JM, "JM", "JAM", "Jamaica" },
154 { JO, "JO", "JOR", "Jordan" },
155 { JP, "JP", "JPN", "Japan" },
156 { KE, "KE", "KEN", "Kenya" },
157 { KG, "KG", "KGZ", "Kyrgyzstan" },
158 { KH, "KH", "KHM", "Cambodia" },
159 { KI, "KI", "KIR", "Kiribati" },
160 { KM, "KM", "COM", "Comoros" },
161 { KN, "KN", "KNA", "Saint Kitts and Nevis" },
162 { KP, "KP", "PRK", "Democratic People's Republic of Korea" },
163 { KR, "KR", "KOR", "Republic of Korea" },
164 { KW, "KW", "KWT", "Kuwait" },
165 { KY, "KY", "CYM", "Cayman Islands" },
166 { KZ, "KZ", "KAZ", "Kazakhstan" },
167 { LA, "LA", "LAO", "Lao People's Democratic Republic" },
168 { LB, "LB", "LBN", "Lebanon" },
169 { LC, "LC", "LCA", "Saint Lucia" },
170 { LI, "LI", "LIE", "Liechtenstein" },
171 { LK, "LK", "LKA", "Sri Lanka" },
172 { LR, "LR", "LBR", "Liberia" },
173 { LS, "LS", "LSO", "Lesotho" },
174 { LT, "LT", "LTU", "Lithuania" },
175 { LU, "LU", "LUX", "Luxembourg" },
176 { LV, "LV", "LVA", "Latvia" },
177 { LY, "LY", "LBY", "Libyan Arab Jamahiriya" },
178 { MA, "MA", "MAR", "Morocco" },
179 { MC, "MC", "MCO", "Monaco" },
180 { MD, "MD", "MDA", "Republic of Moldova" },
181 { ME, "ME", "MNE", "Montenegro" },
182 { MF, "MF", "MAF", "Saint Martin (French part)" },
183 { MG, "MG", "MDG", "Madagascar" },
184 { MH, "MH", "MHL", "Marshall Islands" },
185 { MK, "MK", "MKD", "The Former Yugoslav Republic of Macedonia" },
186 { ML, "ML", "MLI", "Mali" },
187 { MM, "MM", "MMR", "Myanmar" },
188 { MN, "MN", "MNG", "Mongolia" },
189 { MO, "MO", "MAC", "Macao" },
190 { MP, "MP", "MNP", "Northern Mariana Islands" },
191 { MQ, "MQ", "MTQ", "Martinique" },
192 { MR, "MR", "MRT", "Mauritania" },
193 { MS, "MS", "MSR", "Montserrat" },
194 { MT, "MT", "MLT", "Malta" },
195 { MU, "MU", "MUS", "Mauritius" },
196 { MV, "MV", "MDV", "Maldives" },
197 { MW, "MW", "MWI", "Malawi" },
198 { MX, "MX", "MEX", "Mexico" },
199 { MY, "MY", "MYS", "Malaysia" },
200 { MZ, "MZ", "MOZ", "Mozambique" },
201 { NA, "NA", "NAM", "Namibia" },
202 { NC, "NC", "NCL", "New Caledonia" },
203 { NE, "NE", "NER", "Niger" },
204 { NF, "NF", "NFK", "Norfolk Island" },
205 { NG, "NG", "NGA", "Nigeria" },
206 { NI, "NI", "NIC", "Nicaragua" },
207 { NL, "NL", "NLD", "Netherlands" },
208 { NO, "NO", "NOR", "Norway" },
209 { NP, "NP", "NPL", "Nepal" },
210 { NR, "NR", "NRU", "Nauru" },
211 { NU, "NU", "NIU", "Niue" },
212 { NZ, "NZ", "NZL", "New Zealand" },
213 { OM, "OM", "OMN", "Oman" },
214 { PA, "PA", "PAN", "Panama" },
215 { PE, "PE", "PER", "Peru" },
216 { PF, "PF", "PYF", "French Polynesia" },
217 { PG, "PG", "PNG", "Papua New Guinea" },
218 { PH, "PH", "PHL", "Philippines" },
219 { PK, "PK", "PAK", "Pakistan" },
220 { PL, "PL", "POL", "Poland" },
221 { PM, "PM", "SPM", "Saint Pierre and Miquelon" },
222 { PN, "PN", "PCN", "Pitcairn" },
223 { PR, "PR", "PRI", "Puerto Rico" },
224 { PS, "PS", "PSE", "Occupied Palestinian Territory" },
225 { PT, "PT", "PRT", "Portugal" },
226 { PW, "PW", "PLW", "Palau" },
227 { PY, "PY", "PRY", "Paraguay" },
228 { QA, "QA", "QAT", "Qatar" },
229 { RE, "RE", "REU", "Reunion" },
230 { RO, "RO", "ROU", "Romania" },
231 { RS, "RS", "SRB", "Serbia" },
232 { RU, "RU", "RUS", "Russian Federation" },
233 { RW, "RW", "RWA", "Rwanda" },
234 { SA, "SA", "SAU", "Saudi Arabia" },
235 { SB, "SB", "SLB", "Solomon Islands" },
236 { SC, "SC", "SYC", "Seychelles" },
237 { SD, "SD", "SDN", "Sudan" },
238 { SE, "SE", "SWE", "Sweden" },
239 { SG, "SG", "SGP", "Singapore" },
240 { SH, "SH", "SHN", "Saint Helena, Ascension and Tristan da Cunha" },
241 { SI, "SI", "SVN", "Slovenia" },
242 { SJ, "SJ", "534", "Svalbard and Jan Mayen" },
243 { SK, "SK", "SVK", "Slovakia" },
244 { SL, "SL", "SLE", "Sierra Leone" },
245 { SM, "SM", "SMR", "San Marino" },
246 { SN, "SN", "SEN", "Senegal" },
247 { SO, "SO", "SOM", "Somalia" },
248 { SR, "SR", "SUR", "Suriname" },
249 { SS, "SS", "SSD", "South Sudan" },
250 { ST, "ST", "STP", "Sao Tome and Principe" },
251 { SV, "SV", "SLV", "El Salvador" },
252 { SX, "SX", "SXM", "Sint Maarten (Dutch part)" },
253 { SY, "SY", "SYR", "Syrian Arab Republic" },
254 { SZ, "SZ", "SWZ", "Swaziland" },
255 { TC, "TC", "TCA", "Turks and Caicos Islands" },
256 { TD, "TD", "TCD", "Chad" },
257 { TF, "TF", "ATF", "French Southern Territories" },
258 { TG, "TG", "TGO", "Togo" },
259 { TH, "TH", "THA", "Thailand" },
260 { TJ, "TJ", "TJK", "Tajikistan" },
261 { TK, "TK", "TKL", "Tokelau" },
262 { TL, "TL", "TLS", "Timor-Leste" },
263 { TM, "TM", "TKM", "Turkmenistan" },
264 { TN, "TN", "TUN", "Tunisia" },
265 { TO, "TO", "TON", "Tonga" },
266 { TR, "TR", "TUR", "Turkey" },
267 { TT, "TT", "TTO", "Trinidad and Tobago" },
268 { TV, "TV", "TUV", "Tuvalu" },
269 { TW, "TW", "TWN", "Taiwan, Province of China" },
270 { TZ, "TZ", "TZA", "United Republic of Tanzania" },
271 { UA, "UA", "UKR", "Ukraine" },
272 { UG, "UG", "UGA", "Uganda" },
273 { UM, "UM", "UMI", "United States Minor Outlying Islands" },
274 { US, "US", "USA", "United States" },
275 { UY, "UY", "URY", "Uruguay" },
276 { UZ, "UZ", "UZB", "Uzbekistan" },
277 { VA, "VA", "VAT", "Holy See (Vatican City State)" },
278 { VC, "VC", "VCT", "Saint Vincent and The Grenadines" },
279 { VE, "VE", "VEN", "Bolivarian Republic of Venezuela" },
280 { VG, "VG", "VGB", "British Virgin Islands" },
281 { VI, "VI", "VIR", "U.S. Virgin Islands" },
282 { VN, "VN", "VNM", "Viet Nam" },
283 { VU, "VU", "VUT", "Vanuatu" },
284 { WF, "WF", "WLF", "Wallis and Futuna" },
285 { WS, "WS", "WSM", "Samoa" },
286 { YE, "YE", "YEM", "Yemen" },
287 { YT, "YT", "MYT", "Mayotte" },
288 { ZA, "ZA", "ZAF", "South Africa" },
289 { ZM, "ZM", "ZMB", "Zambia" },
290 { ZW, "ZW", "ZWE", "Zimbabwe" },
291 };
292
293 #define COUNTRY_COUNT (sizeof(country_list)/sizeof(struct cCountry))
294
cmp_alpha2(const void * key,const void * b)295 static int cmp_alpha2(const void *key, const void *b)
296 {
297 const struct cCountry *elem = b;
298
299 return strcasecmp(key, elem->alpha2_name);
300 }
301
cmp_alpha3(const void * key,const void * b)302 static int cmp_alpha3(const void *key, const void *b)
303 {
304 const struct cCountry *elem = b;
305
306 return strcasecmp(key, elem->alpha3_name);
307 }
308
309
310 /* convert ISO 3166-1 two-letter constant (alpha-2)
311 * to index number
312 * return 0(COUNTRY_UNKNOWN) if not found.
313 */
dvb_country_a2_to_id(const char * name)314 enum dvb_country_t dvb_country_a2_to_id(const char *name)
315 {
316 const struct cCountry *p;
317
318 p = bsearch(name, country_list,
319 COUNTRY_COUNT, sizeof(country_list[0]), cmp_alpha2);
320 return p ? p->id : COUNTRY_UNKNOWN;
321 }
322
323 /* convert ISO 3166-1 three-letter constant (alpha-3)
324 * to index number
325 * return 0(COUNTRY_UNKNOWN) if not found.
326 */
dvb_country_a3_to_id(const char * name)327 enum dvb_country_t dvb_country_a3_to_id(const char *name)
328 {
329 const struct cCountry *p;
330
331 p = bsearch(name, country_list,
332 COUNTRY_COUNT, sizeof(country_list[0]), cmp_alpha3);
333 return p ? p->id : COUNTRY_UNKNOWN;
334 }
335
336
337 /* convert index number
338 * to ISO 3166-1 two-letter constant
339 * return NULL if not found.
340 */
dvb_country_to_2letters(int idx)341 const char *dvb_country_to_2letters(int idx)
342 {
343 return (idx >= 1 && idx < COUNTRY_COUNT)
344 ? country_list[idx].alpha2_name : NULL;
345 }
346
347 /* convert index number
348 * to ISO 3166-1 three-letter constant
349 * return NULL if not found.
350 */
dvb_country_to_3letters(int idx)351 const char *dvb_country_to_3letters(int idx)
352 {
353 return (idx >= 1 && idx < COUNTRY_COUNT)
354 ? country_list[idx].alpha3_name : NULL;
355 }
356
357 /* convert index number
358 * to country name
359 * return NULL if not found.
360 */
dvb_country_to_name(int idx)361 const char *dvb_country_to_name(int idx)
362 {
363 return (idx >= 1 && idx < COUNTRY_COUNT)
364 ? country_list[idx].short_name : NULL;
365 }
366
367 #ifndef HAVE_SECURE_GETENV
368 # ifdef HAVE___SECURE_GETENV
369 # define secure_getenv __secure_getenv
370 # else
371 # define secure_getenv getenv
372 # endif
373 #endif
374
375 #define MIN(X,Y) (X < Y ? X : Y)
376
377 static const char * cats[] = {
378 "LC_ALL", "LC_CTYPE", "LC_COLLATE", "LC_MESSAGES", "LANG"
379 };
380
dvb_guess_user_country(void)381 enum dvb_country_t dvb_guess_user_country(void)
382 {
383 char * buf, * pch, * pbuf;
384 unsigned cat;
385 enum dvb_country_t id = COUNTRY_UNKNOWN;
386
387 for (cat = 0; cat < sizeof(cats)/sizeof(cats[0]); cat++) {
388
389 // the returned char * should be "C", "POSIX" or something valid.
390 // If valid, we can only *guess* which format is returned.
391 // Assume here something like "de_DE.iso8859-1@euro" or "de_DE.utf-8"
392 buf = secure_getenv(cats[cat]);
393 if (! buf || strlen(buf) < 2)
394 continue;
395
396 if (! strncmp(buf, "POSIX", MIN(strlen(buf), 5)) ||
397 ! (strncmp(buf, "en", MIN(strlen(buf), 2)) && !isalpha(buf[2])) )
398 continue;
399
400 buf = strdup(buf);
401 pbuf= buf;
402
403 // assuming 'language_country.encoding@variant'
404
405 // country after '_', if given
406 if ((pch = strchr(buf, '_')))
407 pbuf = pch + 1;
408
409 // remove all after '@', including '@'
410 if ((pch = strchr(pbuf, '@')))
411 *pch = 0;
412
413 // remove all after '.', including '.'
414 if ((pch = strchr(pbuf, '.')))
415 *pch = 0;
416
417 if (strlen(pbuf) == 2)
418 id = dvb_country_a2_to_id(pbuf);
419 free(buf);
420 if (id != COUNTRY_UNKNOWN)
421 return id;
422 }
423
424 return COUNTRY_UNKNOWN;
425 }
426