1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * locale.c: functions for language/locale configuration
12  */
13 
14 #include "vim.h"
15 
16 #if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
17 	&& (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
18 # define HAVE_GET_LOCALE_VAL
19     static char_u *
get_locale_val(int what)20 get_locale_val(int what)
21 {
22     char_u	*loc;
23 
24     // Obtain the locale value from the libraries.
25     loc = (char_u *)setlocale(what, NULL);
26 
27 # ifdef MSWIN
28     if (loc != NULL)
29     {
30 	char_u	*p;
31 
32 	// setocale() returns something like "LC_COLLATE=<name>;LC_..." when
33 	// one of the values (e.g., LC_CTYPE) differs.
34 	p = vim_strchr(loc, '=');
35 	if (p != NULL)
36 	{
37 	    loc = ++p;
38 	    while (*p != NUL)	// remove trailing newline
39 	    {
40 		if (*p < ' ' || *p == ';')
41 		{
42 		    *p = NUL;
43 		    break;
44 		}
45 		++p;
46 	    }
47 	}
48     }
49 # endif
50 
51     return loc;
52 }
53 #endif
54 
55 
56 #ifdef MSWIN
57 /*
58  * On MS-Windows locale names are strings like "German_Germany.1252", but
59  * gettext expects "de".  Try to translate one into another here for a few
60  * supported languages.
61  */
62     static char_u *
gettext_lang(char_u * name)63 gettext_lang(char_u *name)
64 {
65     int		i;
66     static char *(mtable[]) = {
67 			"afrikaans",	"af",
68 			"czech",	"cs",
69 			"dutch",	"nl",
70 			"german",	"de",
71 			"english_united kingdom", "en_GB",
72 			"spanish",	"es",
73 			"french",	"fr",
74 			"italian",	"it",
75 			"japanese",	"ja",
76 			"korean",	"ko",
77 			"norwegian",	"no",
78 			"polish",	"pl",
79 			"russian",	"ru",
80 			"slovak",	"sk",
81 			"swedish",	"sv",
82 			"ukrainian",	"uk",
83 			"chinese_china", "zh_CN",
84 			"chinese_taiwan", "zh_TW",
85 			NULL};
86 
87     for (i = 0; mtable[i] != NULL; i += 2)
88 	if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
89 	    return (char_u *)mtable[i + 1];
90     return name;
91 }
92 #endif
93 
94 #if defined(FEAT_MULTI_LANG) || defined(PROTO)
95 /*
96  * Return TRUE when "lang" starts with a valid language name.
97  * Rejects NULL, empty string, "C", "C.UTF-8" and others.
98  */
99     static int
is_valid_mess_lang(char_u * lang)100 is_valid_mess_lang(char_u *lang)
101 {
102     return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
103 }
104 
105 /*
106  * Obtain the current messages language.  Used to set the default for
107  * 'helplang'.  May return NULL or an empty string.
108  */
109     char_u *
get_mess_lang(void)110 get_mess_lang(void)
111 {
112     char_u *p;
113 
114 # ifdef HAVE_GET_LOCALE_VAL
115 #  if defined(LC_MESSAGES)
116     p = get_locale_val(LC_MESSAGES);
117 #  else
118     // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
119     // may be set to the LCID number.  LC_COLLATE is the best guess, LC_TIME
120     // and LC_MONETARY may be set differently for a Japanese working in the
121     // US.
122     p = get_locale_val(LC_COLLATE);
123 #  endif
124 # else
125     p = mch_getenv((char_u *)"LC_ALL");
126     if (!is_valid_mess_lang(p))
127     {
128 	p = mch_getenv((char_u *)"LC_MESSAGES");
129 	if (!is_valid_mess_lang(p))
130 	    p = mch_getenv((char_u *)"LANG");
131     }
132 # endif
133 # ifdef MSWIN
134     p = gettext_lang(p);
135 # endif
136     return is_valid_mess_lang(p) ? p : NULL;
137 }
138 #endif
139 
140 // Complicated #if; matches with where get_mess_env() is used below.
141 #if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
142 	    && defined(LC_MESSAGES))) \
143 	|| ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
144 		&& !defined(LC_MESSAGES))
145 /*
146  * Get the language used for messages from the environment.
147  */
148     static char_u *
get_mess_env(void)149 get_mess_env(void)
150 {
151     char_u	*p;
152 
153     p = mch_getenv((char_u *)"LC_ALL");
154     if (p == NULL || *p == NUL)
155     {
156 	p = mch_getenv((char_u *)"LC_MESSAGES");
157 	if (p == NULL || *p == NUL)
158 	{
159 	    p = mch_getenv((char_u *)"LANG");
160 	    if (p != NULL && VIM_ISDIGIT(*p))
161 		p = NULL;		// ignore something like "1043"
162 # ifdef HAVE_GET_LOCALE_VAL
163 	    if (p == NULL || *p == NUL)
164 		p = get_locale_val(LC_CTYPE);
165 # endif
166 	}
167     }
168     return p;
169 }
170 #endif
171 
172 #if defined(FEAT_EVAL) || defined(PROTO)
173 
174 /*
175  * Set the "v:lang" variable according to the current locale setting.
176  * Also do "v:lc_time"and "v:ctype".
177  */
178     void
set_lang_var(void)179 set_lang_var(void)
180 {
181     char_u	*loc;
182 
183 # ifdef HAVE_GET_LOCALE_VAL
184     loc = get_locale_val(LC_CTYPE);
185 # else
186     // setlocale() not supported: use the default value
187     loc = (char_u *)"C";
188 # endif
189     set_vim_var_string(VV_CTYPE, loc, -1);
190 
191     // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
192     // back to LC_CTYPE if it's empty.
193 # if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES)
194     loc = get_locale_val(LC_MESSAGES);
195 # else
196     loc = get_mess_env();
197 # endif
198     set_vim_var_string(VV_LANG, loc, -1);
199 
200 # ifdef HAVE_GET_LOCALE_VAL
201     loc = get_locale_val(LC_TIME);
202 # endif
203     set_vim_var_string(VV_LC_TIME, loc, -1);
204 
205 # ifdef HAVE_GET_LOCALE_VAL
206     loc = get_locale_val(LC_COLLATE);
207 # else
208     // setlocale() not supported: use the default value
209     loc = (char_u *)"C";
210 # endif
211     set_vim_var_string(VV_COLLATE, loc, -1);
212 }
213 #endif
214 
215 #if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
216 /*
217  * Setup to use the current locale (for ctype() and many other things).
218  */
219     void
init_locale(void)220 init_locale(void)
221 {
222     setlocale(LC_ALL, "");
223 
224 # ifdef FEAT_GUI_GTK
225     // Tell Gtk not to change our locale settings.
226     gtk_disable_setlocale();
227 # endif
228 # if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
229     // Make sure strtod() uses a decimal point, not a comma.
230     setlocale(LC_NUMERIC, "C");
231 # endif
232 
233 # ifdef MSWIN
234     // Apparently MS-Windows printf() may cause a crash when we give it 8-bit
235     // text while it's expecting text in the current locale.  This call avoids
236     // that.
237     setlocale(LC_CTYPE, "C");
238 # endif
239 
240 # ifdef FEAT_GETTEXT
241     {
242 	int	mustfree = FALSE;
243 	char_u	*p;
244 
245 #  ifdef DYNAMIC_GETTEXT
246 	// Initialize the gettext library
247 	dyn_libintl_init();
248 #  endif
249 	// expand_env() doesn't work yet, because g_chartab[] is not
250 	// initialized yet, call vim_getenv() directly
251 	p = vim_getenv((char_u *)"VIMRUNTIME", &mustfree);
252 	if (p != NULL && *p != NUL)
253 	{
254 	    vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p);
255 	    bindtextdomain(VIMPACKAGE, (char *)NameBuff);
256 	}
257 	if (mustfree)
258 	    vim_free(p);
259 	textdomain(VIMPACKAGE);
260     }
261 # endif
262 }
263 
264 /*
265  * ":language":  Set the language (locale).
266  */
267     void
ex_language(exarg_T * eap)268 ex_language(exarg_T *eap)
269 {
270     char	*loc;
271     char_u	*p;
272     char_u	*name;
273     int		what = LC_ALL;
274     char	*whatstr = "";
275 # ifdef LC_MESSAGES
276 #  define VIM_LC_MESSAGES LC_MESSAGES
277 # else
278 #  define VIM_LC_MESSAGES 6789
279 # endif
280 
281     name = eap->arg;
282 
283     // Check for "messages {name}", "ctype {name}" or "time {name}" argument.
284     // Allow abbreviation, but require at least 3 characters to avoid
285     // confusion with a two letter language name "me" or "ct".
286     p = skiptowhite(eap->arg);
287     if ((*p == NUL || VIM_ISWHITE(*p)) && p - eap->arg >= 3)
288     {
289 	if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
290 	{
291 	    what = VIM_LC_MESSAGES;
292 	    name = skipwhite(p);
293 	    whatstr = "messages ";
294 	}
295 	else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
296 	{
297 	    what = LC_CTYPE;
298 	    name = skipwhite(p);
299 	    whatstr = "ctype ";
300 	}
301 	else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
302 	{
303 	    what = LC_TIME;
304 	    name = skipwhite(p);
305 	    whatstr = "time ";
306 	}
307 	else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0)
308 	{
309 	    what = LC_COLLATE;
310 	    name = skipwhite(p);
311 	    whatstr = "collate ";
312 	}
313     }
314 
315     if (*name == NUL)
316     {
317 # ifndef LC_MESSAGES
318 	if (what == VIM_LC_MESSAGES)
319 	    p = get_mess_env();
320 	else
321 # endif
322 	    p = (char_u *)setlocale(what, NULL);
323 	if (p == NULL || *p == NUL)
324 	    p = (char_u *)"Unknown";
325 	smsg(_("Current %slanguage: \"%s\""), whatstr, p);
326     }
327     else
328     {
329 # ifndef LC_MESSAGES
330 	if (what == VIM_LC_MESSAGES)
331 	    loc = "";
332 	else
333 # endif
334 	{
335 	    loc = setlocale(what, (char *)name);
336 # if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
337 	    // Make sure strtod() uses a decimal point, not a comma.
338 	    setlocale(LC_NUMERIC, "C");
339 # endif
340 	}
341 	if (loc == NULL)
342 	    semsg(_("E197: Cannot set language to \"%s\""), name);
343 	else
344 	{
345 # ifdef HAVE_NL_MSG_CAT_CNTR
346 	    // Need to do this for GNU gettext, otherwise cached translations
347 	    // will be used again.
348 	    extern int _nl_msg_cat_cntr;
349 
350 	    ++_nl_msg_cat_cntr;
351 # endif
352 	    // Reset $LC_ALL, otherwise it would overrule everything.
353 	    vim_setenv((char_u *)"LC_ALL", (char_u *)"");
354 
355 	    if (what != LC_TIME && what != LC_COLLATE)
356 	    {
357 		// Tell gettext() what to translate to.  It apparently doesn't
358 		// use the currently effective locale.  Also do this when
359 		// FEAT_GETTEXT isn't defined, so that shell commands use this
360 		// value.
361 		if (what == LC_ALL)
362 		{
363 		    vim_setenv((char_u *)"LANG", name);
364 
365 		    // Clear $LANGUAGE because GNU gettext uses it.
366 		    vim_setenv((char_u *)"LANGUAGE", (char_u *)"");
367 # ifdef MSWIN
368 		    // Apparently MS-Windows printf() may cause a crash when
369 		    // we give it 8-bit text while it's expecting text in the
370 		    // current locale.  This call avoids that.
371 		    setlocale(LC_CTYPE, "C");
372 # endif
373 		}
374 		if (what != LC_CTYPE)
375 		{
376 		    char_u	*mname;
377 # ifdef MSWIN
378 		    mname = gettext_lang(name);
379 # else
380 		    mname = name;
381 # endif
382 		    vim_setenv((char_u *)"LC_MESSAGES", mname);
383 # ifdef FEAT_MULTI_LANG
384 		    set_helplang_default(mname);
385 # endif
386 		}
387 	    }
388 
389 # ifdef FEAT_EVAL
390 	    // Set v:lang, v:lc_time, v:collate and v:ctype to the final result.
391 	    set_lang_var();
392 # endif
393 	    maketitle();
394 	}
395     }
396 }
397 
398 static char_u	**locales = NULL;	// Array of all available locales
399 
400 static int	did_init_locales = FALSE;
401 
402 /*
403  * Return an array of strings for all available locales + NULL for the
404  * last element.  Return NULL in case of error.
405  */
406     static char_u **
find_locales(void)407 find_locales(void)
408 {
409     garray_T	locales_ga;
410     char_u	*loc;
411     char_u	*locale_list;
412 # ifdef MSWIN
413     size_t	len = 0;
414 # endif
415 
416     // Find all available locales by running command "locale -a".  If this
417     // doesn't work we won't have completion.
418 # ifndef MSWIN
419     locale_list = get_cmd_output((char_u *)"locale -a",
420 						    NULL, SHELL_SILENT, NULL);
421 # else
422     // Find all available locales by examining the directories in
423     // $VIMRUNTIME/lang/
424     {
425 	int		options = WILD_SILENT|WILD_USE_NL|WILD_KEEP_ALL;
426 	expand_T	xpc;
427 	char_u		*p;
428 
429 	ExpandInit(&xpc);
430 	xpc.xp_context = EXPAND_DIRECTORIES;
431 	locale_list = ExpandOne(&xpc, (char_u *)"$VIMRUNTIME/lang/*",
432 						      NULL, options, WILD_ALL);
433 	ExpandCleanup(&xpc);
434 	if (locale_list == NULL)
435 	    // Add a dummy input, that will be skipped lated but we need to
436 	    // have something in locale_list so that the C locale is added at
437 	    // the end.
438 	    locale_list = vim_strsave((char_u *)".\n");
439 	p = locale_list;
440 	// find the last directory delimiter
441 	while (p != NULL && *p != NUL)
442 	{
443 	    if (*p == '\n')
444 		break;
445 	    if (*p == '\\')
446 		len = p - locale_list;
447 	    p++;
448 	}
449     }
450 # endif
451     if (locale_list == NULL)
452 	return NULL;
453     ga_init2(&locales_ga, sizeof(char_u *), 20);
454 
455     // Transform locale_list string where each locale is separated by "\n"
456     // into an array of locale strings.
457     loc = (char_u *)strtok((char *)locale_list, "\n");
458 
459     while (loc != NULL)
460     {
461 	int ignore = FALSE;
462 
463 # ifdef MSWIN
464 	if (len > 0)
465 	    loc += len + 1;
466 	// skip locales with a dot (which indicates the charset)
467 	if (vim_strchr(loc, '.') != NULL)
468 	    ignore = TRUE;
469 # endif
470 	if (!ignore)
471 	{
472 	    if (ga_grow(&locales_ga, 1) == FAIL)
473 		break;
474 
475 	    loc = vim_strsave(loc);
476 	    if (loc == NULL)
477 		break;
478 
479 	    ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc;
480 	}
481 	loc = (char_u *)strtok(NULL, "\n");
482     }
483 
484 # ifdef MSWIN
485     // Add the C locale
486     if (ga_grow(&locales_ga, 1) == OK)
487 	((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] =
488 						    vim_strsave((char_u *)"C");
489 # endif
490 
491     vim_free(locale_list);
492     if (ga_grow(&locales_ga, 1) == FAIL)
493     {
494 	ga_clear(&locales_ga);
495 	return NULL;
496     }
497     ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
498     return (char_u **)locales_ga.ga_data;
499 }
500 
501 /*
502  * Lazy initialization of all available locales.
503  */
504     static void
init_locales(void)505 init_locales(void)
506 {
507     if (!did_init_locales)
508     {
509 	did_init_locales = TRUE;
510 	locales = find_locales();
511     }
512 }
513 
514 # if defined(EXITFREE) || defined(PROTO)
515     void
free_locales(void)516 free_locales(void)
517 {
518     int			i;
519     if (locales != NULL)
520     {
521 	for (i = 0; locales[i] != NULL; i++)
522 	    vim_free(locales[i]);
523 	VIM_CLEAR(locales);
524     }
525 }
526 # endif
527 
528 /*
529  * Function given to ExpandGeneric() to obtain the possible arguments of the
530  * ":language" command.
531  */
532     char_u *
get_lang_arg(expand_T * xp UNUSED,int idx)533 get_lang_arg(expand_T *xp UNUSED, int idx)
534 {
535     if (idx == 0)
536 	return (char_u *)"messages";
537     if (idx == 1)
538 	return (char_u *)"ctype";
539     if (idx == 2)
540 	return (char_u *)"time";
541     if (idx == 3)
542 	return (char_u *)"collate";
543 
544     init_locales();
545     if (locales == NULL)
546 	return NULL;
547     return locales[idx - 4];
548 }
549 
550 /*
551  * Function given to ExpandGeneric() to obtain the available locales.
552  */
553     char_u *
get_locales(expand_T * xp UNUSED,int idx)554 get_locales(expand_T *xp UNUSED, int idx)
555 {
556     init_locales();
557     if (locales == NULL)
558 	return NULL;
559     return locales[idx];
560 }
561 
562 #endif
563