1 /* gettext.c - gettext module */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/types.h>
21 #include <grub/misc.h>
25 #include <grub/normal.h>
26 #include <grub/file.h>
27 #include <grub/kernel.h>
28 #include <grub/i18n.h>
30 GRUB_MOD_LICENSE ("GPLv3+");
33 .mo file information from:
34 http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html .
37 static struct grub_gettext_context main_context
, secondary_context
;
39 static const char *(*grub_gettext_original
) (const char *s
);
41 struct grub_gettext_msg
50 grub_uint32_t version
;
51 grub_uint32_t number_of_strings
;
52 grub_uint32_t offset_original
;
53 grub_uint32_t offset_translation
;
56 struct string_descriptor
62 struct grub_gettext_context
65 grub_off_t grub_gettext_offset_original
;
66 grub_off_t grub_gettext_offset_translation
;
67 grub_size_t grub_gettext_max
;
68 int grub_gettext_max_log
;
69 struct grub_gettext_msg
*grub_gettext_msg_list
;
72 #define MO_MAGIC_NUMBER 0x950412de
75 grub_gettext_pread (grub_file_t file
, void *buf
, grub_size_t len
,
80 if (grub_file_seek (file
, offset
) == (grub_off_t
) - 1)
82 if (grub_file_read (file
, buf
, len
) != (grub_ssize_t
) len
)
85 grub_error (GRUB_ERR_READ_ERROR
, N_("premature end of file"));
92 grub_gettext_getstr_from_position (struct grub_gettext_context
*ctx
,
96 grub_off_t internal_position
;
100 struct string_descriptor desc
;
103 internal_position
= (off
+ position
* sizeof (desc
));
105 err
= grub_gettext_pread (ctx
->fd_mo
, (char *) &desc
,
106 sizeof (desc
), internal_position
);
109 length
= grub_cpu_to_le32 (desc
.length
);
110 offset
= grub_cpu_to_le32 (desc
.offset
);
112 translation
= grub_malloc (length
+ 1);
116 err
= grub_gettext_pread (ctx
->fd_mo
, translation
, length
, offset
);
119 grub_free (translation
);
122 translation
[length
] = '\0';
128 grub_gettext_gettranslation_from_position (struct grub_gettext_context
*ctx
,
129 grub_size_t position
)
131 if (!ctx
->grub_gettext_msg_list
[position
].translated
)
132 ctx
->grub_gettext_msg_list
[position
].translated
133 = grub_gettext_getstr_from_position (ctx
,
134 ctx
->grub_gettext_offset_translation
,
136 return ctx
->grub_gettext_msg_list
[position
].translated
;
140 grub_gettext_getstring_from_position (struct grub_gettext_context
*ctx
,
141 grub_size_t position
)
143 if (!ctx
->grub_gettext_msg_list
[position
].name
)
144 ctx
->grub_gettext_msg_list
[position
].name
145 = grub_gettext_getstr_from_position (ctx
,
146 ctx
->grub_gettext_offset_original
,
148 return ctx
->grub_gettext_msg_list
[position
].name
;
152 grub_gettext_translate_real (struct grub_gettext_context
*ctx
,
155 grub_size_t current
= 0;
157 const char *current_string
;
158 static int depth
= 0;
160 if (!ctx
->grub_gettext_msg_list
|| !ctx
->fd_mo
)
163 /* Shouldn't happen. Just a precaution if our own code
164 calls gettext somehow. */
169 /* Make sure we can use grub_gettext_translate for error messages. Push
170 active error message to error stack and reset error message. */
173 for (i
= ctx
->grub_gettext_max_log
; i
>= 0; i
--)
178 test
= current
| (1 << i
);
179 if (test
>= ctx
->grub_gettext_max
)
182 current_string
= grub_gettext_getstring_from_position (ctx
, test
);
186 grub_errno
= GRUB_ERR_NONE
;
192 /* Search by bisection. */
193 cmp
= grub_strcmp (current_string
, orig
);
199 ret
= grub_gettext_gettranslation_from_position (ctx
, current
);
202 grub_errno
= GRUB_ERR_NONE
;
213 if (current
== 0 && ctx
->grub_gettext_max
!= 0)
215 current_string
= grub_gettext_getstring_from_position (ctx
, 0);
219 grub_errno
= GRUB_ERR_NONE
;
225 if (grub_strcmp (current_string
, orig
) == 0)
228 ret
= grub_gettext_gettranslation_from_position (ctx
, current
);
231 grub_errno
= GRUB_ERR_NONE
;
248 grub_gettext_translate (const char *orig
)
254 ret
= grub_gettext_translate_real (&main_context
, orig
);
257 ret
= grub_gettext_translate_real (&secondary_context
, orig
);
264 grub_gettext_delete_list (struct grub_gettext_context
*ctx
)
266 struct grub_gettext_msg
*l
= ctx
->grub_gettext_msg_list
;
271 ctx
->grub_gettext_msg_list
= 0;
272 for (i
= 0; i
< ctx
->grub_gettext_max
; i
++)
273 grub_free (l
[i
].name
);
274 /* Don't delete the translated message because could be in use. */
277 grub_file_close (ctx
->fd_mo
);
279 grub_memset (ctx
, 0, sizeof (*ctx
));
282 /* This is similar to grub_file_open. */
284 grub_mofile_open (struct grub_gettext_context
*ctx
,
285 const char *filename
)
291 /* Using fd_mo and not another variable because
292 it's needed for grub_gettext_get_info. */
294 fd
= grub_file_open (filename
);
299 err
= grub_gettext_pread (fd
, &head
, sizeof (head
), 0);
302 grub_file_close (fd
);
306 if (head
.magic
!= grub_cpu_to_le32_compile_time (MO_MAGIC_NUMBER
))
308 grub_file_close (fd
);
309 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
310 "mo: invalid mo magic in file: %s", filename
);
313 if (head
.version
!= 0)
315 grub_file_close (fd
);
316 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
317 "mo: invalid mo version in file: %s", filename
);
320 ctx
->grub_gettext_offset_original
= grub_le_to_cpu32 (head
.offset_original
);
321 ctx
->grub_gettext_offset_translation
= grub_le_to_cpu32 (head
.offset_translation
);
322 ctx
->grub_gettext_max
= grub_le_to_cpu32 (head
.number_of_strings
);
323 for (ctx
->grub_gettext_max_log
= 0; ctx
->grub_gettext_max
>> ctx
->grub_gettext_max_log
;
324 ctx
->grub_gettext_max_log
++);
326 ctx
->grub_gettext_msg_list
= grub_zalloc (ctx
->grub_gettext_max
327 * sizeof (ctx
->grub_gettext_msg_list
[0]));
328 if (!ctx
->grub_gettext_msg_list
)
330 grub_file_close (fd
);
334 if (grub_gettext
!= grub_gettext_translate
)
336 grub_gettext_original
= grub_gettext
;
337 grub_gettext
= grub_gettext_translate
;
342 /* Returning grub_file_t would be more natural, but grub_mofile_open assigns
343 to fd_mo anyway ... */
345 grub_mofile_open_lang (struct grub_gettext_context
*ctx
,
346 const char *part1
, const char *part2
, const char *locale
)
351 /* mo_file e.g.: /boot/grub/locale/ca.mo */
353 mo_file
= grub_xasprintf ("%s%s/%s.mo", part1
, part2
, locale
);
357 err
= grub_mofile_open (ctx
, mo_file
);
359 /* Will try adding .gz as well. */
363 grub_errno
= GRUB_ERR_NONE
;
364 mo_file_old
= mo_file
;
365 mo_file
= grub_xasprintf ("%s.gz", mo_file
);
366 grub_free (mo_file_old
);
369 err
= grub_mofile_open (ctx
, mo_file
);
375 grub_gettext_init_ext (struct grub_gettext_context
*ctx
,
377 const char *locale_dir
, const char *prefix
)
379 const char *part1
, *part2
;
382 grub_gettext_delete_list (ctx
);
384 if (!locale
|| locale
[0] == 0)
389 if (!part1
|| part1
[0] == 0)
395 if (!part1
|| part1
[0] == 0)
398 err
= grub_mofile_open_lang (ctx
, part1
, part2
, locale
);
400 /* ll_CC didn't work, so try ll. */
403 char *lang
= grub_strdup (locale
);
404 char *underscore
= lang
? grub_strchr (lang
, '_') : 0;
409 grub_errno
= GRUB_ERR_NONE
;
410 err
= grub_mofile_open_lang (ctx
, part1
, part2
, lang
);
419 grub_gettext_env_write_lang (struct grub_env_var
*var
420 __attribute__ ((unused
)), const char *val
)
423 err
= grub_gettext_init_ext (&main_context
, val
, grub_env_get ("locale_dir"),
424 grub_env_get ("prefix"));
428 err
= grub_gettext_init_ext (&secondary_context
, val
,
429 grub_env_get ("secondary_locale_dir"), 0);
433 return grub_strdup (val
);
437 grub_gettext_reread_prefix (const char *val
)
440 err
= grub_gettext_init_ext (&main_context
, grub_env_get ("lang"),
441 grub_env_get ("locale_dir"),
448 read_main (struct grub_env_var
*var
449 __attribute__ ((unused
)), const char *val
)
452 err
= grub_gettext_init_ext (&main_context
, grub_env_get ("lang"), val
,
453 grub_env_get ("prefix"));
456 return grub_strdup (val
);
460 read_secondary (struct grub_env_var
*var
461 __attribute__ ((unused
)), const char *val
)
464 err
= grub_gettext_init_ext (&secondary_context
, grub_env_get ("lang"), val
,
469 return grub_strdup (val
);
473 grub_cmd_translate (grub_command_t cmd
__attribute__ ((unused
)),
474 int argc
, char **args
)
477 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("one argument expected"));
479 const char *translation
;
480 translation
= grub_gettext_translate (args
[0]);
481 grub_printf ("%s\n", translation
);
485 GRUB_MOD_INIT (gettext
)
490 lang
= grub_env_get ("lang");
492 err
= grub_gettext_init_ext (&main_context
, lang
, grub_env_get ("locale_dir"),
493 grub_env_get ("prefix"));
496 err
= grub_gettext_init_ext (&secondary_context
, lang
,
497 grub_env_get ("secondary_locale_dir"), 0);
501 grub_register_variable_hook ("locale_dir", NULL
, read_main
);
502 grub_register_variable_hook ("secondary_locale_dir", NULL
, read_secondary
);
504 grub_register_command_p1 ("gettext", grub_cmd_translate
,
506 /* TRANSLATORS: It refers to passing the string through gettext.
507 So it's "translate" in the same meaning as in what you're
510 N_("Translates the string with the current settings."));
512 /* Reload .mo file information if lang changes. */
513 grub_register_variable_hook ("lang", NULL
, grub_gettext_env_write_lang
);
515 /* Preserve hooks after context changes. */
516 grub_env_export ("lang");
517 grub_env_export ("locale_dir");
518 grub_env_export ("secondary_locale_dir");
521 GRUB_MOD_FINI (gettext
)
523 grub_gettext_delete_list (&main_context
);
524 grub_gettext_delete_list (&secondary_context
);
526 grub_gettext
= grub_gettext_original
;