Line | Count | Source (jump to first uncovered line) |
1 | | /* i18n.c - gettext initialization |
2 | | * Copyright (C) 2007, 2010 Free Software Foundation, Inc. |
3 | | * Copyright (C) 2015 g10 Code GmbH |
4 | | * |
5 | | * This file is free software; you can redistribute it and/or modify |
6 | | * it under the terms of either |
7 | | * |
8 | | * - the GNU Lesser General Public License as published by the Free |
9 | | * Software Foundation; either version 3 of the License, or (at |
10 | | * your option) any later version. |
11 | | * |
12 | | * or |
13 | | * |
14 | | * - the GNU General Public License as published by the Free |
15 | | * Software Foundation; either version 2 of the License, or (at |
16 | | * your option) any later version. |
17 | | * |
18 | | * or both in parallel, as here. |
19 | | * |
20 | | * This file is distributed in the hope that it will be useful, |
21 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 | | * GNU General Public License for more details. |
24 | | * |
25 | | * You should have received a copy of the GNU General Public License |
26 | | * along with this program; if not, see <https://www.gnu.org/licenses/>. |
27 | | */ |
28 | | |
29 | | #include <config.h> |
30 | | #ifdef HAVE_LOCALE_H |
31 | | #include <locale.h> |
32 | | #endif |
33 | | #ifdef HAVE_LANGINFO_CODESET |
34 | | #include <langinfo.h> |
35 | | #endif |
36 | | |
37 | | #include "util.h" |
38 | | #include "i18n.h" |
39 | | |
40 | | |
41 | | #undef USE_MSGCACHE |
42 | | #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) \ |
43 | | && !defined(USE_SIMPLE_GETTEXT) && defined(ENABLE_NLS) |
44 | | # define USE_MSGCACHE 1 |
45 | | #endif |
46 | | |
47 | | |
48 | | #ifdef USE_MSGCACHE |
49 | | /* An object to store pointers to static strings and their static |
50 | | translations. A linked list is not optimal but given that we only |
51 | | have a few dozen messages it should be acceptable. */ |
52 | | struct msg_cache_s |
53 | | { |
54 | | struct msg_cache_s *next; |
55 | | const char *key; |
56 | | const char *value; |
57 | | }; |
58 | | |
59 | | /* A object to store an lc_messages string and a link to the cache |
60 | | object. */ |
61 | | struct msg_cache_heads_s |
62 | | { |
63 | | struct msg_cache_heads_s *next; |
64 | | struct msg_cache_s *cache; |
65 | | char lc_messages[1]; |
66 | | }; |
67 | | |
68 | | /* Out static cache of translated messages. We need this because |
69 | | there is no gettext API to return a translation depending on the |
70 | | locale. Switching the locale for each access to a translatable |
71 | | string seems to be too expensive. Note that this is used only for |
72 | | strings in gpg-agent which are passed to Pinentry. All other |
73 | | strings are using the regular gettext interface. Note that we can |
74 | | never release this memory because consumers take the result as |
75 | | static strings. */ |
76 | | static struct msg_cache_heads_s *msgcache; |
77 | | |
78 | | #endif /*USE_MSGCACHE*/ |
79 | | |
80 | | |
81 | | void |
82 | | i18n_init (void) |
83 | 0 | { |
84 | | #ifdef USE_SIMPLE_GETTEXT |
85 | | bindtextdomain (PACKAGE_GT, gnupg_localedir ()); |
86 | | textdomain (PACKAGE_GT); |
87 | | #else |
88 | 0 | # ifdef ENABLE_NLS |
89 | 0 | setlocale (LC_ALL, "" ); |
90 | 0 | bindtextdomain (PACKAGE_GT, gnupg_localedir ()); |
91 | 0 | textdomain (PACKAGE_GT); |
92 | 0 | # endif |
93 | 0 | #endif |
94 | 0 | } |
95 | | |
96 | | |
97 | | /* The Assuan agent protocol requires us to transmit utf-8 strings |
98 | | thus we need a way to temporary switch gettext from native to |
99 | | utf8. */ |
100 | | char * |
101 | | i18n_switchto_utf8 (void) |
102 | 118k | { |
103 | | #ifdef USE_SIMPLE_GETTEXT |
104 | | /* Return an arbitrary pointer as true value. */ |
105 | | return gettext_use_utf8 (1) ? (char*)(-1) : NULL; |
106 | | #elif defined(ENABLE_NLS) |
107 | 118k | char *orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL); |
108 | 118k | # ifdef HAVE_LANGINFO_CODESET |
109 | 118k | if (!orig_codeset) |
110 | 1 | orig_codeset = nl_langinfo (CODESET); |
111 | 118k | # endif |
112 | 118k | if (orig_codeset) |
113 | 118k | { /* We only switch when we are able to restore the codeset later. |
114 | | Note that bind_textdomain_codeset does only return on memory |
115 | | errors but not if a codeset is not available. Thus we don't |
116 | | bother printing a diagnostic here. */ |
117 | 118k | orig_codeset = xstrdup (orig_codeset); |
118 | 118k | if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8")) |
119 | 0 | { |
120 | 0 | xfree (orig_codeset); |
121 | 0 | orig_codeset = NULL; |
122 | 0 | } |
123 | 118k | } |
124 | 118k | return orig_codeset; |
125 | | #else |
126 | | return NULL; |
127 | | #endif |
128 | 118k | } |
129 | | |
130 | | /* Switch back to the saved codeset. */ |
131 | | void |
132 | | i18n_switchback (char *saved_codeset) |
133 | 118k | { |
134 | | #ifdef USE_SIMPLE_GETTEXT |
135 | | gettext_use_utf8 (!!saved_codeset); |
136 | | #elif defined(ENABLE_NLS) |
137 | 118k | if (saved_codeset) |
138 | 118k | { |
139 | 118k | bind_textdomain_codeset (PACKAGE_GT, saved_codeset); |
140 | 118k | xfree (saved_codeset); |
141 | 118k | } |
142 | | #else |
143 | | (void)saved_codeset; |
144 | | #endif |
145 | 118k | } |
146 | | |
147 | | |
148 | | /* Gettext variant which temporary switches to utf-8 for string. */ |
149 | | const char * |
150 | | i18n_utf8 (const char *string) |
151 | 0 | { |
152 | 0 | char *saved = i18n_switchto_utf8 (); |
153 | 0 | const char *result = _(string); |
154 | 0 | i18n_switchback (saved); |
155 | 0 | return result; |
156 | 0 | } |
157 | | |
158 | | |
159 | | /* A variant of gettext which allows the programmer to specify the |
160 | | locale to use for translating the message. The function assumes |
161 | | that utf-8 is used for the encoding. */ |
162 | | const char * |
163 | | i18n_localegettext (const char *lc_messages, const char *string) |
164 | 0 | { |
165 | 0 | #if USE_MSGCACHE |
166 | 0 | const char *result = NULL; |
167 | 0 | char *saved = NULL; |
168 | 0 | struct msg_cache_heads_s *mh; |
169 | 0 | struct msg_cache_s *mc; |
170 | |
|
171 | 0 | if (!lc_messages) |
172 | 0 | goto leave; |
173 | | |
174 | | /* Lookup in the cache. */ |
175 | 0 | for (mh = msgcache; mh; mh = mh->next) |
176 | 0 | if (!strcmp (mh->lc_messages, lc_messages)) |
177 | 0 | break; |
178 | 0 | if (mh) |
179 | 0 | { |
180 | | /* A cache entry for this local exists - find the string. |
181 | | Because the system is designed for static strings it is |
182 | | sufficient to compare the pointers. */ |
183 | 0 | for (mc = mh->cache; mc; mc = mc->next) |
184 | 0 | if (mc->key == string) |
185 | 0 | { |
186 | | /* Cache hit. */ |
187 | 0 | result = mc->value; |
188 | 0 | goto leave; |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | /* Cached miss. Change the locale, translate, reset locale. */ |
193 | 0 | saved = setlocale (LC_MESSAGES, NULL); |
194 | 0 | if (!saved) |
195 | 0 | goto leave; |
196 | 0 | saved = xtrystrdup (saved); |
197 | 0 | if (!saved) |
198 | 0 | goto leave; |
199 | 0 | if (!setlocale (LC_MESSAGES, lc_messages)) |
200 | 0 | goto leave; |
201 | | |
202 | 0 | bindtextdomain (PACKAGE_GT, gnupg_localedir ()); |
203 | 0 | result = gettext (string); |
204 | 0 | setlocale (LC_MESSAGES, saved); |
205 | 0 | bindtextdomain (PACKAGE_GT, gnupg_localedir ()); |
206 | | |
207 | | /* Cache the result. */ |
208 | 0 | if (!mh) |
209 | 0 | { |
210 | | /* First use of this locale - create an entry. */ |
211 | 0 | mh = xtrymalloc (sizeof *mh + strlen (lc_messages)); |
212 | 0 | if (!mh) |
213 | 0 | goto leave; |
214 | 0 | strcpy (mh->lc_messages, lc_messages); |
215 | 0 | mh->cache = NULL; |
216 | 0 | mh->next = msgcache; |
217 | 0 | msgcache = mh; |
218 | 0 | } |
219 | 0 | mc = xtrymalloc (sizeof *mc); |
220 | 0 | if (!mc) |
221 | 0 | goto leave; |
222 | 0 | mc->key = string; |
223 | 0 | mc->value = result; |
224 | 0 | mc->next = mh->cache; |
225 | 0 | mh->cache = mc; |
226 | |
|
227 | 0 | leave: |
228 | 0 | xfree (saved); |
229 | 0 | return result? result : _(string); |
230 | |
|
231 | | #else /*!USE_MSGCACHE*/ |
232 | | |
233 | | (void)lc_messages; |
234 | | return _(string); |
235 | | |
236 | | #endif /*!USE_MSGCACHE*/ |
237 | 0 | } |