1 /* language.c
2  * (c) 2002 Mikulas Patocka
3  * This file is a part of the Links program, released under GPL.
4  */
5 
6 #include "links.h"
7 
8 struct translation {
9 	const char *name;
10 };
11 
12 struct translation_desc {
13 	const struct translation *t;
14 };
15 
16 unsigned char dummyarray[T__N_TEXTS];
17 
18 #include "language.inc"
19 
20 static unsigned char **translation_array[N_LANGUAGES][N_CODEPAGES];
21 
22 int current_language;
23 static int current_lang_charset;
24 
init_trans(void)25 void init_trans(void)
26 {
27 	int i, j;
28 	for (i = 0; i < N_LANGUAGES; i++)
29 		for (j = 0; j < N_CODEPAGES; j++)
30 			translation_array[i][j] = NULL;
31 	set_language(-1);
32 }
33 
shutdown_trans(void)34 void shutdown_trans(void)
35 {
36 	int i, j, k;
37 	for (i = 0; i < N_LANGUAGES; i++)
38 		for (j = 0; j < N_CODEPAGES; j++) if (translation_array[i][j]) {
39 			for (k = 0; k < T__N_TEXTS; k++) {
40 				unsigned char *txt = translation_array[i][j][k];
41 				if (txt &&
42 				    txt != cast_uchar translations[i].t[k].name &&
43 				    txt != cast_uchar translation_english[k].name)
44 					mem_free(txt);
45 			}
46 			mem_free(translation_array[i][j]);
47 		}
48 }
49 
get_language_from_lang(unsigned char * lang)50 int get_language_from_lang(unsigned char *lang)
51 {
52 	unsigned char *p;
53 	int i;
54 	lang = stracpy(lang);
55 	lang[strcspn(cast_const_char lang, ".@")] = 0;
56 	if (!casestrcmp(lang, cast_uchar "nn_NO"))
57 		strcpy(cast_char lang, "no");
58 	for (p = lang; *p; p++) {
59 		if (*p >= 'A' && *p <= 'Z')
60 			*p += 'a' - 'A';
61 		if (*p == '_')
62 			*p = '-';
63 	}
64 search_again:
65 	for (i = 0; i < n_languages(); i++) {
66 		p = cast_uchar translations[i].t[T__ACCEPT_LANGUAGE].name;
67 		if (!p)
68 			continue;
69 		p = stracpy(p);
70 		p[strcspn(cast_const_char p, ",;")] = 0;
71 		if (!casestrcmp(lang, p)) {
72 			mem_free(p);
73 			mem_free(lang);
74 			return i;
75 		}
76 		mem_free(p);
77 	}
78 	if ((p = cast_uchar strchr(cast_const_char lang, '-'))) {
79 		*p = 0;
80 		goto search_again;
81 	}
82 	mem_free(lang);
83 	return -1;
84 }
85 
get_default_language(void)86 int get_default_language(void)
87 {
88 	static int default_language = -1;
89 	unsigned char *lang;
90 
91 	if (default_language >= 0)
92 		return default_language;
93 
94 	default_language = os_default_language();
95 	if (default_language >= 0)
96 		return default_language;
97 
98 	lang = cast_uchar getenv("LANG");
99 	if (lang) {
100 		default_language = get_language_from_lang(lang);
101 		if (default_language >= 0)
102 			return default_language;
103 	}
104 
105 	default_language = get_language_from_lang(cast_uchar "en");
106 	if (default_language < 0)
107 		internal_error("default language 'english' not found");
108 	return default_language;
109 }
110 
get_current_language(void)111 int get_current_language(void)
112 {
113 	if (current_language >= 0)
114 		return current_language;
115 	return get_default_language();
116 }
117 
get_default_charset(void)118 int get_default_charset(void)
119 {
120 	static int default_charset = -1;
121 	unsigned char *lang, *p;
122 	int i;
123 	if (default_charset >= 0)
124 		return default_charset;
125 
126 	i = os_default_charset();
127 	if (i >= 0)
128 		goto ret_i;
129 
130 	lang = cast_uchar getenv("LC_CTYPE");
131 	if (!lang)
132 		lang = cast_uchar getenv("LANG");
133 	if (!lang) {
134 		i = 0;
135 		goto ret_i;
136 	}
137 	if ((p = cast_uchar strchr(cast_const_char lang, '.'))) {
138 		p++;
139 	} else if ((i = get_cp_index(lang)) >= 0) {
140 		goto ret_i;
141 	} else {
142 		if (strlen(cast_const_char lang) > 5 && !casestrcmp(cast_uchar (strchr(cast_const_char lang, 0) - 5), cast_uchar "@euro")) {
143 			p = cast_uchar "ISO-8859-15";
144 		} else {
145 			int def_lang = get_language_from_lang(lang);
146 			if (def_lang < 0) {
147 				i = 0;
148 				goto ret_i;
149 			}
150 			p = cast_uchar translations[def_lang].t[T__DEFAULT_CHAR_SET].name;
151 			if (!p)
152 				p = cast_uchar "";
153 		}
154 	}
155 	i = get_cp_index(p);
156 
157 	if (i < 0)
158 		i = 0;
159 
160 	ret_i:
161 #ifndef ENABLE_UTF8
162 	if (!F && i == utf8_table)
163 		i = 0;
164 #endif
165 	default_charset = i;
166 	return i;
167 }
168 
get_commandline_charset(void)169 int get_commandline_charset(void)
170 {
171 	return dump_codepage == -1 ? get_default_charset() : dump_codepage;
172 }
173 
get_text_index(unsigned char * text)174 static inline int get_text_index(unsigned char *text)
175 {
176 	unsigned char * volatile dmv = dummyarray;
177 	unsigned char * dm = dmv;
178 	if (((my_uintptr_t)text >= (my_uintptr_t)dm && (my_uintptr_t)text < (my_uintptr_t)(dm + T__N_TEXTS)))
179 		return (int)(text - dm);
180 	return -1;
181 }
182 
get_text_translation(unsigned char * text,struct terminal * term)183 unsigned char *get_text_translation(unsigned char *text, struct terminal *term)
184 {
185 	unsigned char **current_tra;
186 	unsigned char *trn;
187 	int charset;
188 	int language_idx = get_current_language();
189 	int idx;
190 	if (!term) charset = 0;
191 	else charset = term_charset(term);
192 	idx = get_text_index(text);
193 	if (idx < 0) return text;
194 	if ((current_tra = translation_array[language_idx][charset])) {
195 		unsigned char *tt;
196 		if ((trn = current_tra[idx])) return trn;
197 		tr:
198 		if (!(tt = cast_uchar translations[language_idx].t[idx].name)) {
199 			trn = cast_uchar translation_english[idx].name;
200 		} else {
201 			struct document_options l_opt;
202 			memset(&l_opt, 0, sizeof(l_opt));
203 			l_opt.plain = 0;
204 			l_opt.cp = charset;
205 			trn = convert(current_lang_charset, charset, tt, &l_opt);
206 			if (!strcmp(cast_const_char trn, cast_const_char tt)) {
207 				mem_free(trn);
208 				trn = tt;
209 			}
210 		}
211 		current_tra[idx] = trn;
212 	} else {
213 		if (current_lang_charset && charset != current_lang_charset) {
214 			current_tra = translation_array[language_idx][charset] = mem_alloc(sizeof (unsigned char *) * T__N_TEXTS);
215 			memset(current_tra, 0, sizeof (unsigned char *) * T__N_TEXTS);
216 			goto tr;
217 		}
218 		if (!(trn = cast_uchar translations[language_idx].t[idx].name)) {
219 			trn = cast_uchar translation_english[idx].name;
220 		}
221 	}
222 	return trn;
223 }
224 
get_english_translation(unsigned char * text)225 unsigned char *get_english_translation(unsigned char *text)
226 {
227 	int idx = get_text_index(text);
228 	if (idx < 0) return text;
229 	return cast_uchar translation_english[idx].name;
230 }
231 
n_languages(void)232 int n_languages(void)
233 {
234 	return N_LANGUAGES;
235 }
236 
language_name(int l)237 unsigned char *language_name(int l)
238 {
239 	if (l == -1) return cast_uchar "default";
240 	return cast_uchar translations[l].t[T__LANGUAGE].name;
241 }
242 
set_language(int l)243 void set_language(int l)
244 {
245 	int i;
246 	unsigned char *cp;
247 	current_language = l;
248 	l = get_current_language();
249 	cp = cast_uchar translations[l].t[T__CHAR_SET].name;
250 	i = get_cp_index(cp);
251 	if (i == -1) {
252 		internal_error("Unknown charset for language %s.", translations[l].t[T__LANGUAGE].name);
253 		i = 0;
254 	}
255 	current_lang_charset = i;
256 }
257 
258 #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) && defined(LC_MESSAGES)
259 #define USE_OLD_LOCALE
260 #endif
261 #if defined(HAVE_FREELOCALE) && defined(HAVE_NEWLOCALE) && defined(HAVE_STRERROR_L)
262 #define USE_NEW_LOCALE
263 #endif
264 
strerror_alloc_internal(int err,void * loc_p)265 static unsigned char *strerror_alloc_internal(int err, void *loc_p)
266 {
267 	unsigned char *strerror_buf, *e;
268 	unsigned size = 32;
269 larger_buf:
270 	strerror_buf = mem_alloc(size);
271 	if (!loc_p)
272 		e = cast_uchar strerror(err);
273 	else
274 #ifdef USE_NEW_LOCALE
275 		e = cast_uchar strerror_l(err, *(locale_t *)loc_p);
276 #else
277 		e = NULL;
278 #endif
279 	if (!e)
280 		e = cast_uchar "Unknown error";
281 	if (strlen(cast_const_char e) >= size) {
282 		size *= 2;
283 		if (!size) overalloc();
284 		mem_free(strerror_buf);
285 		goto larger_buf;
286 	}
287 	strcpy(cast_char strerror_buf, cast_const_char e);
288 	return strerror_buf;
289 }
290 
291 #ifdef USE_OLD_LOCALE
292 
293 #ifdef USE_NEW_LOCALE
294 
strerror_try_locale(int err,unsigned char * lc,int charset_from,int charset_to)295 static unsigned char *strerror_try_locale(int err, unsigned char *lc, int charset_from, int charset_to)
296 {
297 	locale_t nl;
298 	unsigned char *str, *str_converted;
299 	nl = newlocale(LC_CTYPE_MASK | LC_MESSAGES_MASK, cast_const_char lc, (locale_t)0);
300 	if (nl == (locale_t)0)
301 		return NULL;
302 	str = strerror_alloc_internal(err, &nl);
303 	freelocale(nl);
304 	str_converted = convert(charset_from, charset_to, str, NULL);
305 	mem_free(str);
306 	return str_converted;
307 }
308 
309 #else
310 
strerror_try_locale(int err,unsigned char * lc,int charset_from,int charset_to)311 static unsigned char *strerror_try_locale(int err, unsigned char *lc, int charset_from, int charset_to)
312 {
313 	unsigned char *prev_type;
314 	unsigned char *prev_msg;
315 	unsigned char *str;
316 	unsigned char *str_converted = NULL;
317 
318 	prev_type = cast_uchar setlocale(LC_CTYPE, NULL);
319 	if (!prev_type)
320 		goto err0;
321 	prev_type = stracpy(prev_type);
322 	prev_msg = cast_uchar setlocale(LC_MESSAGES, NULL);
323 	if (!prev_msg)
324 		goto err1;
325 	prev_msg = stracpy(prev_msg);
326 
327 	if (!setlocale(LC_CTYPE, cast_const_char lc))
328 		goto err2;
329 	if (!setlocale(LC_MESSAGES, cast_const_char lc))
330 		goto err2;
331 
332 	str = strerror_alloc_internal(err, NULL);
333 	str_converted = convert(charset_from, charset_to, str, NULL);
334 	mem_free(str);
335 
336 err2:
337 	setlocale(LC_MESSAGES, cast_const_char prev_msg);
338 	mem_free(prev_msg);
339 err1:
340 	setlocale(LC_CTYPE, cast_const_char prev_type);
341 	mem_free(prev_type);
342 err0:
343 	return str_converted;
344 }
345 
346 #endif
347 
strerror_try_charset(int err,unsigned char * lc,unsigned char * charset_from,int charset_to)348 static unsigned char *strerror_try_charset(int err, unsigned char *lc, unsigned char *charset_from, int charset_to)
349 {
350 	unsigned char *full, *ret;
351 	int idx;
352 
353 	idx = get_cp_index(charset_from);
354 	if (idx < 0)
355 		return NULL;
356 	full = stracpy(lc);
357 	add_to_strn(&full, cast_uchar ".");
358 	add_to_strn(&full, charset_from);
359 
360 	ret = strerror_try_locale(err, full, idx, charset_to);
361 
362 	mem_free(full);
363 	return ret;
364 }
365 
strerror_try_language(int err,unsigned char * lc,unsigned char * lc_charset,int charset_to)366 static unsigned char *strerror_try_language(int err, unsigned char *lc, unsigned char *lc_charset, int charset_to)
367 {
368 #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 5
369 	return strerror_try_charset(err, lc, cast_uchar "UTF-8", charset_to);
370 #else
371 	unsigned char *result;
372 	if (charset_to == utf8_table) {
373 		result = strerror_try_charset(err, lc, cast_uchar "UTF-8", charset_to);
374 		if (result)
375 			return result;
376 		result = strerror_try_charset(err, lc, lc_charset, charset_to);
377 		if (result)
378 			return result;
379 	} else {
380 		result = strerror_try_charset(err, lc, lc_charset, charset_to);
381 		if (result)
382 			return result;
383 		result = strerror_try_charset(err, lc, cast_uchar "UTF-8", charset_to);
384 		if (result)
385 			return result;
386 	}
387 	return NULL;
388 #endif
389 }
390 
strerror_alloc(int err,struct terminal * term)391 unsigned char *strerror_alloc(int err, struct terminal *term)
392 {
393 	int charset_to;
394 	unsigned char *lc_str, *lc_charset;
395 	if (term) {
396 		charset_to = term_charset(term);
397 	} else {
398 		charset_to = 0;
399 	}
400 	lc_str = get_text_translation(TEXT_(T__LOCALE_CODE), term);
401 	lc_charset = get_text_translation(TEXT_(T__DEFAULT_CHAR_SET), term);
402 	while (*lc_str) {
403 		int l = (int)strcspn(cast_const_char lc_str, ",");
404 		unsigned char *lc = memacpy(lc_str, l);
405 		unsigned char *result;
406 
407 		result = strerror_try_language(err, lc, lc_charset, charset_to);
408 		mem_free(lc);
409 		if (result)
410 			return result;
411 
412 		lc_str += l;
413 		lc_str += *lc_str == ',';
414 	}
415 	return strerror_alloc_internal(err, NULL);
416 }
417 
418 #else
419 
strerror_alloc(int err,struct terminal * term)420 unsigned char *strerror_alloc(int err, struct terminal *term)
421 {
422 	return strerror_alloc_internal(err, NULL);
423 }
424 
425 #endif
426