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