1 /*
2  * Copyright (c) 2010 Ævar Arnfjörð Bjarmason
3  */
4 
5 #include "cache.h"
6 #include "exec-cmd.h"
7 #include "gettext.h"
8 #include "strbuf.h"
9 #include "utf8.h"
10 #include "config.h"
11 
12 #ifndef NO_GETTEXT
13 #	include <locale.h>
14 #	include <libintl.h>
15 #	ifdef GIT_WINDOWS_NATIVE
16 
locale_charset(void)17 static const char *locale_charset(void)
18 {
19 	const char *env = getenv("LC_ALL"), *dot;
20 
21 	if (!env || !*env)
22 		env = getenv("LC_CTYPE");
23 	if (!env || !*env)
24 		env = getenv("LANG");
25 
26 	if (!env)
27 		return "UTF-8";
28 
29 	dot = strchr(env, '.');
30 	return !dot ? env : dot + 1;
31 }
32 
33 #	elif defined HAVE_LIBCHARSET_H
34 #		include <libcharset.h>
35 #	else
36 #		include <langinfo.h>
37 #		define locale_charset() nl_langinfo(CODESET)
38 #	endif
39 #endif
40 
41 static const char *charset;
42 
43 /*
44  * Guess the user's preferred languages from the value in LANGUAGE environment
45  * variable and LC_MESSAGES locale category if NO_GETTEXT is not defined.
46  *
47  * The result can be a colon-separated list like "ko:ja:en".
48  */
get_preferred_languages(void)49 const char *get_preferred_languages(void)
50 {
51 	const char *retval;
52 
53 	retval = getenv("LANGUAGE");
54 	if (retval && *retval)
55 		return retval;
56 
57 #ifndef NO_GETTEXT
58 	retval = setlocale(LC_MESSAGES, NULL);
59 	if (retval && *retval &&
60 		strcmp(retval, "C") &&
61 		strcmp(retval, "POSIX"))
62 		return retval;
63 #endif
64 
65 	return NULL;
66 }
67 
68 #ifndef NO_GETTEXT
69 __attribute__((format (printf, 1, 2)))
test_vsnprintf(const char * fmt,...)70 static int test_vsnprintf(const char *fmt, ...)
71 {
72 	char buf[26];
73 	int ret;
74 	va_list ap;
75 	va_start(ap, fmt);
76 	ret = vsnprintf(buf, sizeof(buf), fmt, ap);
77 	va_end(ap);
78 	return ret;
79 }
80 
init_gettext_charset(const char * domain)81 static void init_gettext_charset(const char *domain)
82 {
83 	setlocale(LC_CTYPE, "");
84 	charset = locale_charset();
85 	bind_textdomain_codeset(domain, charset);
86 
87 	/*
88 	 * Work around an old bug fixed in glibc 2.17 (released on
89 	 * 2012-12-24), at the cost of potentially making translated
90 	 * messages from external functions like perror() emitted in
91 	 * the wrong encoding.
92 	 *
93 	 * The bug affected e.g. git.git's own 7eb93c89651 ([PATCH]
94 	 * Simplify git script, 2005-09-07), which is the origin of
95 	 * the "David_K\345gedal" test string.
96 	 *
97 	 * See a much longer comment added to this file in 5e9637c6297
98 	 * (i18n: add infrastructure for translating Git with gettext,
99 	 * 2011-11-18) for more details.
100 	 */
101 	if (test_vsnprintf("%.*s", 13, "David_K\345gedal") < 0)
102 		setlocale(LC_CTYPE, "C");
103 }
104 
git_setup_gettext(void)105 void git_setup_gettext(void)
106 {
107 	const char *podir = getenv(GIT_TEXT_DOMAIN_DIR_ENVIRONMENT);
108 	char *p = NULL;
109 
110 	if (!podir)
111 		podir = p = system_path(GIT_LOCALE_PATH);
112 
113 	if (!is_directory(podir)) {
114 		free(p);
115 		return;
116 	}
117 
118 	bindtextdomain("git", podir);
119 	setlocale(LC_MESSAGES, "");
120 	setlocale(LC_TIME, "");
121 	init_gettext_charset("git");
122 	textdomain("git");
123 
124 	free(p);
125 }
126 
127 /* return the number of columns of string 's' in current locale */
gettext_width(const char * s)128 int gettext_width(const char *s)
129 {
130 	static int is_utf8 = -1;
131 	if (is_utf8 == -1)
132 		is_utf8 = is_utf8_locale();
133 
134 	return is_utf8 ? utf8_strwidth(s) : strlen(s);
135 }
136 #endif
137 
is_utf8_locale(void)138 int is_utf8_locale(void)
139 {
140 #ifdef NO_GETTEXT
141 	if (!charset) {
142 		const char *env = getenv("LC_ALL");
143 		if (!env || !*env)
144 			env = getenv("LC_CTYPE");
145 		if (!env || !*env)
146 			env = getenv("LANG");
147 		if (!env)
148 			env = "";
149 		if (strchr(env, '.'))
150 			env = strchr(env, '.') + 1;
151 		charset = xstrdup(env);
152 	}
153 #endif
154 	return is_encoding_utf8(charset);
155 }
156