chiark / gitweb /
dirmngr: Drop useless housekeeping.
[gnupg2.git] / common / i18n.c
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 {
84 #ifdef USE_SIMPLE_GETTEXT
85   bindtextdomain (PACKAGE_GT, gnupg_localedir ());
86   textdomain (PACKAGE_GT);
87 #else
88 # ifdef ENABLE_NLS
89   setlocale (LC_ALL, "" );
90   bindtextdomain (PACKAGE_GT, LOCALEDIR);
91   textdomain (PACKAGE_GT);
92 # endif
93 #endif
94 }
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 {
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   char *orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
108 # ifdef HAVE_LANGINFO_CODESET
109   if (!orig_codeset)
110     orig_codeset = nl_langinfo (CODESET);
111 # endif
112   if (orig_codeset)
113     { /* 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       orig_codeset = xstrdup (orig_codeset);
118       if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
119         {
120           xfree (orig_codeset);
121           orig_codeset = NULL;
122         }
123     }
124   return orig_codeset;
125 #else
126   return NULL;
127 #endif
128 }
129
130 /* Switch back to the saved codeset.  */
131 void
132 i18n_switchback (char *saved_codeset)
133 {
134 #ifdef USE_SIMPLE_GETTEXT
135   gettext_use_utf8 (!!saved_codeset);
136 #elif defined(ENABLE_NLS)
137   if (saved_codeset)
138     {
139       bind_textdomain_codeset (PACKAGE_GT, saved_codeset);
140       xfree (saved_codeset);
141     }
142 #else
143   (void)saved_codeset;
144 #endif
145 }
146
147
148 /* Gettext variant which temporary switches to utf-8 for string. */
149 const char *
150 i18n_utf8 (const char *string)
151 {
152   char *saved = i18n_switchto_utf8 ();
153   const char *result = _(string);
154   i18n_switchback (saved);
155   return result;
156 }
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 {
165 #if USE_MSGCACHE
166   const char *result = NULL;
167   char *saved = NULL;
168   struct msg_cache_heads_s *mh;
169   struct msg_cache_s *mc;
170
171   if (!lc_messages)
172     goto leave;
173
174   /* Lookup in the cache.  */
175   for (mh = msgcache; mh; mh = mh->next)
176     if (!strcmp (mh->lc_messages, lc_messages))
177       break;
178   if (mh)
179     {
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       for (mc = mh->cache; mc; mc = mc->next)
184         if (mc->key == string)
185           {
186             /* Cache hit.  */
187             result = mc->value;
188             goto leave;
189           }
190     }
191
192   /* Cached miss.  Change the locale, translate, reset locale.  */
193   saved = setlocale (LC_MESSAGES, NULL);
194   if (!saved)
195     goto leave;
196   saved = xtrystrdup (saved);
197   if (!saved)
198     goto leave;
199   if (!setlocale (LC_MESSAGES, lc_messages))
200     goto leave;
201
202   bindtextdomain (PACKAGE_GT, LOCALEDIR);
203   result = gettext (string);
204   setlocale (LC_MESSAGES, saved);
205   bindtextdomain (PACKAGE_GT, LOCALEDIR);
206
207   /* Cache the result.  */
208   if (!mh)
209     {
210       /* First use of this locale - create an entry.  */
211       mh = xtrymalloc (sizeof *mh + strlen (lc_messages));
212       if (!mh)
213         goto leave;
214       strcpy (mh->lc_messages, lc_messages);
215       mh->cache = NULL;
216       mh->next = msgcache;
217       msgcache = mh;
218     }
219   mc = xtrymalloc (sizeof *mc);
220   if (!mc)
221     goto leave;
222   mc->key = string;
223   mc->value = result;
224   mc->next = mh->cache;
225   mh->cache = mc;
226
227  leave:
228   xfree (saved);
229   return result? result : _(string);
230
231 #else /*!USE_MSGCACHE*/
232
233   (void)lc_messages;
234   return _(string);
235
236 #endif /*!USE_MSGCACHE*/
237 }