]> git.proxmox.com Git - grub2.git/blame - gettext/gettext.c
Acount for transfer->size being size-1 when counting *actual
[grub2.git] / gettext / gettext.c
CommitLineData
203ffbfa
CPE
1/* gettext.c - gettext module */
2/*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2009 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
98d3dc02 20#include <grub/list.h>
203ffbfa
CPE
21#include <grub/types.h>
22#include <grub/misc.h>
23#include <grub/mm.h>
24#include <grub/err.h>
25#include <grub/dl.h>
26#include <grub/normal.h>
27#include <grub/file.h>
28#include <grub/kernel.h>
0648f857 29#include <grub/gzio.h>
4a8572e9 30#include <grub/i18n.h>
203ffbfa 31
a2c1332b
FZ
32/*
33 .mo file information from:
203ffbfa
CPE
34 http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html .
35*/
36
37
203ffbfa
CPE
38static grub_file_t fd_mo;
39
40static int grub_gettext_offsetoriginal;
41static int grub_gettext_max;
42
0648f857 43static const char *(*grub_gettext_original) (const char *s);
203ffbfa 44
98d3dc02
CPE
45struct grub_gettext_msg
46{
47 struct grub_gettext_msg *next;
48 const char *name;
49
50 const char *translated;
51};
52
53struct grub_gettext_msg *grub_gettext_msg_list = NULL;
54
0648f857
CPE
55#define GETTEXT_MAGIC_NUMBER 0
56#define GETTEXT_FILE_FORMAT 4
57#define GETTEXT_NUMBER_OF_STRINGS 8
58#define GETTEXT_OFFSET_ORIGINAL 12
59#define GETTEXT_OFFSET_TRANSLATION 16
203ffbfa 60
0648f857 61#define MO_MAGIC_NUMBER 0x950412de
203ffbfa 62
e5fb78c6
CPE
63static grub_ssize_t
64grub_gettext_pread (grub_file_t file, void *buf, grub_size_t len,
65 grub_off_t offset)
66{
67 if (grub_file_seek (file, offset) == (grub_off_t) - 1)
68 {
69 return -1;
70 }
71 return grub_file_read (file, buf, len);
72}
73
203ffbfa
CPE
74static grub_uint32_t
75grub_gettext_get_info (int offset)
76{
77 grub_uint32_t value;
78
e5fb78c6 79 grub_gettext_pread (fd_mo, (char *) &value, 4, offset);
0648f857 80
203ffbfa
CPE
81 value = grub_cpu_to_le32 (value);
82 return value;
83}
84
85static void
0648f857
CPE
86grub_gettext_getstring_from_offset (grub_uint32_t offset,
87 grub_uint32_t length, char *translation)
203ffbfa 88{
e5fb78c6 89 grub_gettext_pread (fd_mo, translation, length, offset);
203ffbfa
CPE
90 translation[length] = '\0';
91}
92
98d3dc02 93static const char *
203ffbfa
CPE
94grub_gettext_gettranslation_from_position (int position)
95{
96 int offsettranslation;
97 int internal_position;
98 grub_uint32_t length, offset;
99 char *translation;
100
101 offsettranslation = grub_gettext_get_info (GETTEXT_OFFSET_TRANSLATION);
102
103 internal_position = offsettranslation + position * 8;
104
e5fb78c6 105 grub_gettext_pread (fd_mo, (char *) &length, 4, internal_position);
203ffbfa 106 length = grub_cpu_to_le32 (length);
0648f857 107
e5fb78c6 108 grub_gettext_pread (fd_mo, (char *) &offset, 4, internal_position + 4);
203ffbfa
CPE
109 offset = grub_cpu_to_le32 (offset);
110
0648f857 111 translation = grub_malloc (length + 1);
203ffbfa
CPE
112 grub_gettext_getstring_from_offset (offset, length, translation);
113
114 return translation;
115}
116
0648f857 117static char *
203ffbfa
CPE
118grub_gettext_getstring_from_position (int position)
119{
120 int internal_position;
121 int length, offset;
122 char *original;
123
124 /* Get position for string i. */
125 internal_position = grub_gettext_offsetoriginal + (position * 8);
126
127 /* Get the length of the string i. */
e5fb78c6 128 grub_gettext_pread (fd_mo, (char *) &length, 4, internal_position);
203ffbfa
CPE
129
130 /* Get the offset of the string i. */
e5fb78c6 131 grub_gettext_pread (fd_mo, (char *) &offset, 4, internal_position + 4);
203ffbfa
CPE
132
133 /* Get the string i. */
134 original = grub_malloc (length + 1);
135 grub_gettext_getstring_from_offset (offset, length, original);
136
137 return original;
138}
139
0648f857 140static const char *
203ffbfa
CPE
141grub_gettext_translate (const char *orig)
142{
143 char *current_string;
98d3dc02 144 const char *ret;
203ffbfa 145
0648f857 146 int min, max, current;
98d3dc02
CPE
147 int found = 0;
148
149 struct grub_gettext_msg *cur;
150
ba2f6848 151 /* Make sure we can use grub_gettext_translate for error messages. Push
152 active error message to error stack and reset error message. */
153 grub_error_push ();
154
98d3dc02
CPE
155 cur = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_gettext_msg_list),
156 orig);
157
158 if (cur)
ba2f6848 159 {
160 grub_error_pop ();
161 return cur->translated;
162 }
203ffbfa
CPE
163
164 if (fd_mo == 0)
ba2f6848 165 {
166 grub_error_pop ();
167 return orig;
168 }
203ffbfa
CPE
169
170 min = 0;
171 max = grub_gettext_max;
172
173 current = (max + min) / 2;
174
98d3dc02 175 while (current != min && current != max && found == 0)
203ffbfa
CPE
176 {
177 current_string = grub_gettext_getstring_from_position (current);
178
179 /* Search by bisection. */
180 if (grub_strcmp (current_string, orig) < 0)
0648f857
CPE
181 {
182 grub_free (current_string);
183 min = current;
184 }
203ffbfa 185 else if (grub_strcmp (current_string, orig) > 0)
0648f857
CPE
186 {
187 grub_free (current_string);
188 max = current;
189 }
203ffbfa 190 else if (grub_strcmp (current_string, orig) == 0)
0648f857
CPE
191 {
192 grub_free (current_string);
98d3dc02 193 found = 1;
0648f857
CPE
194 }
195 current = (max + min) / 2;
203ffbfa
CPE
196 }
197
98d3dc02
CPE
198 ret = found ? grub_gettext_gettranslation_from_position (current) : orig;
199
200 if (found)
201 {
202 cur = grub_zalloc (sizeof (*cur));
203
204 if (cur)
205 {
206 cur->name = grub_strdup (orig);
207 if (cur->name)
208 {
209 cur->translated = ret;
210 grub_list_push (GRUB_AS_LIST_P (&grub_gettext_msg_list),
211 GRUB_AS_LIST (cur));
212 }
213 }
214 else
215 grub_errno = GRUB_ERR_NONE;
216 }
217
ba2f6848 218 grub_error_pop ();
203ffbfa
CPE
219 return ret;
220}
221
222/* This is similar to grub_gzfile_open. */
223static grub_file_t
224grub_mofile_open (const char *filename)
225{
226 int unsigned magic;
227 int version;
228
229 /* Using fd_mo and not another variable because
230 it's needed for grub_gettext_get_info. */
231
c505aa62 232 fd_mo = grub_gzfile_open (filename, 1);
0648f857
CPE
233 grub_errno = GRUB_ERR_NONE;
234
c505aa62 235 if (!fd_mo)
203ffbfa 236 {
b6c871b0 237 grub_dprintf ("gettext", "Cannot read %s\n", filename);
203ffbfa
CPE
238 return 0;
239 }
240
241 magic = grub_gettext_get_info (GETTEXT_MAGIC_NUMBER);
242
243 if (magic != MO_MAGIC_NUMBER)
244 {
0648f857
CPE
245 grub_error (GRUB_ERR_BAD_FILE_TYPE, "mo: invalid mo file: %s",
246 filename);
203ffbfa
CPE
247 grub_file_close (fd_mo);
248 fd_mo = 0;
249 return 0;
250 }
0648f857 251
203ffbfa
CPE
252 version = grub_gettext_get_info (GETTEXT_FILE_FORMAT);
253
254 if (version != 0)
255 {
0648f857 256 grub_error (GRUB_ERR_BAD_FILE_TYPE,
7fd0baee 257 "mo: invalid mo version in file: %s", filename);
203ffbfa
CPE
258 fd_mo = 0;
259 return 0;
260 }
203ffbfa
CPE
261
262 return fd_mo;
263}
264
265static void
266grub_gettext_init_ext (const char *lang)
267{
268 char *mo_file;
a239a5e9 269 char *locale_dir;
203ffbfa 270
a239a5e9 271 locale_dir = grub_env_get ("locale_dir");
0648f857
CPE
272 if (locale_dir == NULL)
273 {
98d3dc02 274 grub_dprintf ("gettext", "locale_dir variable is not set up.\n");
0648f857
CPE
275 return;
276 }
277
278 fd_mo = NULL;
279
ee99edc8 280 /* mo_file e.g.: /boot/grub/locale/ca.mo */
203ffbfa 281
61eb45ee 282 mo_file = grub_xasprintf ("%s/%s.mo", locale_dir, lang);
8b442f3f
VS
283 if (!mo_file)
284 return;
a239a5e9 285
0648f857 286 fd_mo = grub_mofile_open (mo_file);
203ffbfa 287
0648f857
CPE
288 /* Will try adding .gz as well. */
289 if (fd_mo == NULL)
290 {
8b442f3f 291 grub_free (mo_file);
61eb45ee 292 mo_file = grub_xasprintf ("%s.gz", mo_file);
8b442f3f
VS
293 if (!mo_file)
294 return;
0648f857
CPE
295 fd_mo = grub_mofile_open (mo_file);
296 }
203ffbfa
CPE
297
298 if (fd_mo)
299 {
0648f857
CPE
300 grub_gettext_offsetoriginal =
301 grub_gettext_get_info (GETTEXT_OFFSET_ORIGINAL);
302 grub_gettext_max = grub_gettext_get_info (GETTEXT_NUMBER_OF_STRINGS);
203ffbfa
CPE
303
304 grub_gettext_original = grub_gettext;
305 grub_gettext = grub_gettext_translate;
306 }
307}
308
98d3dc02 309static void
90d1e879 310grub_gettext_delete_list (void)
98d3dc02 311{
0959e5ec 312 while (grub_gettext_msg_list)
98d3dc02 313 {
0959e5ec
VS
314 grub_free ((char *) grub_gettext_msg_list->name);
315 grub_gettext_msg_list = grub_gettext_msg_list->next;
ba2f6848 316 /* Don't delete the translated message because could be in use. */
98d3dc02
CPE
317 }
318}
319
0648f857
CPE
320static char *
321grub_gettext_env_write_lang (struct grub_env_var *var
322 __attribute__ ((unused)), const char *val)
203ffbfa
CPE
323{
324 grub_gettext_init_ext (val);
325
98d3dc02
CPE
326 grub_gettext_delete_list ();
327
203ffbfa
CPE
328 return grub_strdup (val);
329}
330
b6c871b0
CPE
331static grub_err_t
332grub_cmd_translate (grub_command_t cmd __attribute__ ((unused)),
333 int argc, char **args)
334{
335 if (argc != 1)
336 return grub_error (GRUB_ERR_BAD_ARGUMENT, "text to translate required");
337
338 const char *translation;
339 translation = grub_gettext_translate (args[0]);
340 grub_printf ("%s\n", translation);
341 return 0;
342}
343
0648f857 344GRUB_MOD_INIT (gettext)
203ffbfa 345{
0648f857
CPE
346 (void) mod; /* To stop warning. */
347
203ffbfa
CPE
348 const char *lang;
349
0648f857 350 lang = grub_env_get ("lang");
203ffbfa
CPE
351
352 grub_gettext_init_ext (lang);
353
b6c871b0 354 grub_register_command_p1 ("gettext", grub_cmd_translate,
5ce0a83a 355 N_("STRING"),
356 N_("Translates the string with the current settings."));
b6c871b0 357
203ffbfa
CPE
358 /* Reload .mo file information if lang changes. */
359 grub_register_variable_hook ("lang", NULL, grub_gettext_env_write_lang);
360
361 /* Preserve hooks after context changes. */
362 grub_env_export ("lang");
363}
364
0648f857 365GRUB_MOD_FINI (gettext)
203ffbfa
CPE
366{
367 if (fd_mo != 0)
0648f857 368 grub_file_close (fd_mo);
203ffbfa 369
98d3dc02
CPE
370 grub_gettext_delete_list ();
371
203ffbfa
CPE
372 grub_gettext = grub_gettext_original;
373}