Coverage Report

Created: 2026-01-17 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/common/i18n.c
Line
Count
Source
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
# ifdef ENABLE_NLS
89
  setlocale (LC_ALL, "" );
90
  bindtextdomain (PACKAGE_GT, gnupg_localedir ());
91
  textdomain (PACKAGE_GT);
92
# 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
0
{
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
0
  return NULL;
127
0
#endif
128
0
}
129
130
/* Switch back to the saved codeset.  */
131
void
132
i18n_switchback (char *saved_codeset)
133
0
{
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
0
  (void)saved_codeset;
144
0
#endif
145
0
}
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
#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, gnupg_localedir ());
203
  result = gettext (string);
204
  setlocale (LC_MESSAGES, saved);
205
  bindtextdomain (PACKAGE_GT, gnupg_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
0
  (void)lc_messages;
234
0
  return _(string);
235
236
0
#endif /*!USE_MSGCACHE*/
237
0
}