Coverage Report

Created: 2022-12-08 06:09

/src/gnupg/common/i18n.c
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
}