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 const char *(*grub_gettext_original
) (const char *s
);
39 struct grub_gettext_msg
48 grub_uint32_t version
;
49 grub_uint32_t number_of_strings
;
50 grub_uint32_t offset_original
;
51 grub_uint32_t offset_translation
;
54 struct string_descriptor
60 struct grub_gettext_context
63 grub_off_t grub_gettext_offset_original
;
64 grub_off_t grub_gettext_offset_translation
;
65 grub_size_t grub_gettext_max
;
66 int grub_gettext_max_log
;
67 struct grub_gettext_msg
*grub_gettext_msg_list
;
70 static struct grub_gettext_context main_context
, secondary_context
;
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
);
360 /* Will try adding .gz as well. */
363 grub_errno
= GRUB_ERR_NONE
;
364 mo_file
= grub_xasprintf ("%s%s/%s.mo.gz", part1
, part2
, locale
);
367 err
= grub_mofile_open (ctx
, mo_file
);
371 /* Will try adding .gmo as well. */
374 grub_errno
= GRUB_ERR_NONE
;
375 mo_file
= grub_xasprintf ("%s%s/%s.gmo", part1
, part2
, locale
);
378 err
= grub_mofile_open (ctx
, mo_file
);
386 grub_gettext_init_ext (struct grub_gettext_context
*ctx
,
388 const char *locale_dir
, const char *prefix
)
390 const char *part1
, *part2
;
393 grub_gettext_delete_list (ctx
);
395 if (!locale
|| locale
[0] == 0)
400 if (!part1
|| part1
[0] == 0)
406 if (!part1
|| part1
[0] == 0)
409 err
= grub_mofile_open_lang (ctx
, part1
, part2
, locale
);
411 /* ll_CC didn't work, so try ll. */
414 char *lang
= grub_strdup (locale
);
415 char *underscore
= lang
? grub_strchr (lang
, '_') : 0;
420 grub_errno
= GRUB_ERR_NONE
;
421 err
= grub_mofile_open_lang (ctx
, part1
, part2
, lang
);
427 if (locale
[0] == 'e' && locale
[1] == 'n'
428 && (locale
[2] == '\0' || locale
[2] == '_'))
429 grub_errno
= err
= GRUB_ERR_NONE
;
434 grub_gettext_env_write_lang (struct grub_env_var
*var
435 __attribute__ ((unused
)), const char *val
)
438 err
= grub_gettext_init_ext (&main_context
, val
, grub_env_get ("locale_dir"),
439 grub_env_get ("prefix"));
443 err
= grub_gettext_init_ext (&secondary_context
, val
,
444 grub_env_get ("secondary_locale_dir"), 0);
448 return grub_strdup (val
);
452 grub_gettext_reread_prefix (const char *val
)
455 err
= grub_gettext_init_ext (&main_context
, grub_env_get ("lang"),
456 grub_env_get ("locale_dir"),
463 read_main (struct grub_env_var
*var
464 __attribute__ ((unused
)), const char *val
)
467 err
= grub_gettext_init_ext (&main_context
, grub_env_get ("lang"), val
,
468 grub_env_get ("prefix"));
471 return grub_strdup (val
);
475 read_secondary (struct grub_env_var
*var
476 __attribute__ ((unused
)), const char *val
)
479 err
= grub_gettext_init_ext (&secondary_context
, grub_env_get ("lang"), val
,
484 return grub_strdup (val
);
488 grub_cmd_translate (grub_command_t cmd
__attribute__ ((unused
)),
489 int argc
, char **args
)
492 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("one argument expected"));
494 const char *translation
;
495 translation
= grub_gettext_translate (args
[0]);
496 grub_printf ("%s\n", translation
);
500 GRUB_MOD_INIT (gettext
)
505 lang
= grub_env_get ("lang");
507 err
= grub_gettext_init_ext (&main_context
, lang
, grub_env_get ("locale_dir"),
508 grub_env_get ("prefix"));
511 err
= grub_gettext_init_ext (&secondary_context
, lang
,
512 grub_env_get ("secondary_locale_dir"), 0);
516 grub_register_variable_hook ("locale_dir", NULL
, read_main
);
517 grub_register_variable_hook ("secondary_locale_dir", NULL
, read_secondary
);
519 grub_register_command_p1 ("gettext", grub_cmd_translate
,
521 /* TRANSLATORS: It refers to passing the string through gettext.
522 So it's "translate" in the same meaning as in what you're
525 N_("Translates the string with the current settings."));
527 /* Reload .mo file information if lang changes. */
528 grub_register_variable_hook ("lang", NULL
, grub_gettext_env_write_lang
);
530 /* Preserve hooks after context changes. */
531 grub_env_export ("lang");
532 grub_env_export ("locale_dir");
533 grub_env_export ("secondary_locale_dir");
536 GRUB_MOD_FINI (gettext
)
538 grub_gettext_delete_list (&main_context
);
539 grub_gettext_delete_list (&secondary_context
);
541 grub_gettext
= grub_gettext_original
;