123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- /* gettext.c - gettext module */
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2009 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/list.h>
- #include <grub/types.h>
- #include <grub/misc.h>
- #include <grub/mm.h>
- #include <grub/err.h>
- #include <grub/dl.h>
- #include <grub/env.h>
- #include <grub/command.h>
- #include <grub/file.h>
- #include <grub/kernel.h>
- #include <grub/gzio.h>
- #include <grub/i18n.h>
- /*
- .mo file information from:
- http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html .
- */
- static grub_file_t fd_mo;
- static int grub_gettext_offsetoriginal;
- static int grub_gettext_max;
- static const char *(*grub_gettext_original) (const char *s);
- struct grub_gettext_msg
- {
- struct grub_gettext_msg *next;
- const char *name;
- const char *translated;
- };
- struct grub_gettext_msg *grub_gettext_msg_list = NULL;
- #define GETTEXT_MAGIC_NUMBER 0
- #define GETTEXT_FILE_FORMAT 4
- #define GETTEXT_NUMBER_OF_STRINGS 8
- #define GETTEXT_OFFSET_ORIGINAL 12
- #define GETTEXT_OFFSET_TRANSLATION 16
- #define MO_MAGIC_NUMBER 0x950412de
- static grub_ssize_t
- grub_gettext_pread (grub_file_t file, void *buf, grub_size_t len,
- grub_off_t offset)
- {
- if (grub_file_seek (file, offset) == (grub_off_t) - 1)
- {
- return -1;
- }
- return grub_file_read (file, buf, len);
- }
- static grub_uint32_t
- grub_gettext_get_info (int offset)
- {
- grub_uint32_t value;
- grub_gettext_pread (fd_mo, (char *) &value, 4, offset);
- value = grub_cpu_to_le32 (value);
- return value;
- }
- static void
- grub_gettext_getstring_from_offset (grub_uint32_t offset,
- grub_uint32_t length, char *translation)
- {
- grub_gettext_pread (fd_mo, translation, length, offset);
- translation[length] = '\0';
- }
- static const char *
- grub_gettext_gettranslation_from_position (int position)
- {
- int offsettranslation;
- int internal_position;
- grub_uint32_t length, offset;
- char *translation;
- offsettranslation = grub_gettext_get_info (GETTEXT_OFFSET_TRANSLATION);
- internal_position = offsettranslation + position * 8;
- grub_gettext_pread (fd_mo, (char *) &length, 4, internal_position);
- length = grub_cpu_to_le32 (length);
- grub_gettext_pread (fd_mo, (char *) &offset, 4, internal_position + 4);
- offset = grub_cpu_to_le32 (offset);
- translation = grub_malloc (length + 1);
- grub_gettext_getstring_from_offset (offset, length, translation);
- return translation;
- }
- static char *
- grub_gettext_getstring_from_position (int position)
- {
- int internal_position;
- int length, offset;
- char *original;
- /* Get position for string i. */
- internal_position = grub_gettext_offsetoriginal + (position * 8);
- /* Get the length of the string i. */
- grub_gettext_pread (fd_mo, (char *) &length, 4, internal_position);
- /* Get the offset of the string i. */
- grub_gettext_pread (fd_mo, (char *) &offset, 4, internal_position + 4);
- /* Get the string i. */
- original = grub_malloc (length + 1);
- grub_gettext_getstring_from_offset (offset, length, original);
- return original;
- }
- static const char *
- grub_gettext_translate (const char *orig)
- {
- char *current_string;
- const char *ret;
- int min, max, current;
- int found = 0;
- struct grub_gettext_msg *cur;
- /* Make sure we can use grub_gettext_translate for error messages. Push
- active error message to error stack and reset error message. */
- grub_error_push ();
- cur = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_gettext_msg_list),
- orig);
- if (cur)
- {
- grub_error_pop ();
- return cur->translated;
- }
- if (fd_mo == 0)
- {
- grub_error_pop ();
- return orig;
- }
- min = 0;
- max = grub_gettext_max;
- current = (max + min) / 2;
- while (current != min && current != max && found == 0)
- {
- current_string = grub_gettext_getstring_from_position (current);
- /* Search by bisection. */
- if (grub_strcmp (current_string, orig) < 0)
- {
- grub_free (current_string);
- min = current;
- }
- else if (grub_strcmp (current_string, orig) > 0)
- {
- grub_free (current_string);
- max = current;
- }
- else if (grub_strcmp (current_string, orig) == 0)
- {
- grub_free (current_string);
- found = 1;
- }
- current = (max + min) / 2;
- }
- ret = found ? grub_gettext_gettranslation_from_position (current) : orig;
- if (found)
- {
- cur = grub_zalloc (sizeof (*cur));
- if (cur)
- {
- cur->name = grub_strdup (orig);
- if (cur->name)
- {
- cur->translated = ret;
- grub_list_push (GRUB_AS_LIST_P (&grub_gettext_msg_list),
- GRUB_AS_LIST (cur));
- }
- }
- else
- grub_errno = GRUB_ERR_NONE;
- }
- grub_error_pop ();
- return ret;
- }
- /* This is similar to grub_gzfile_open. */
- static grub_file_t
- grub_mofile_open (const char *filename)
- {
- int unsigned magic;
- int version;
- /* Using fd_mo and not another variable because
- it's needed for grub_gettext_get_info. */
- fd_mo = grub_gzfile_open (filename, 1);
- grub_errno = GRUB_ERR_NONE;
- if (!fd_mo)
- {
- grub_dprintf ("gettext", "Cannot read %s\n", filename);
- return 0;
- }
- magic = grub_gettext_get_info (GETTEXT_MAGIC_NUMBER);
- if (magic != MO_MAGIC_NUMBER)
- {
- grub_error (GRUB_ERR_BAD_FILE_TYPE, "mo: invalid mo file: %s",
- filename);
- grub_file_close (fd_mo);
- fd_mo = 0;
- return 0;
- }
- version = grub_gettext_get_info (GETTEXT_FILE_FORMAT);
- if (version != 0)
- {
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "mo: invalid mo version in file: %s", filename);
- fd_mo = 0;
- return 0;
- }
- return fd_mo;
- }
- static void
- grub_gettext_init_ext (const char *lang)
- {
- char *mo_file;
- char *locale_dir;
- locale_dir = grub_env_get ("locale_dir");
- if (locale_dir == NULL)
- {
- grub_dprintf ("gettext", "locale_dir variable is not set up.\n");
- return;
- }
- fd_mo = NULL;
- /* mo_file e.g.: /boot/grub/locale/ca.mo */
- mo_file = grub_xasprintf ("%s/%s.mo", locale_dir, lang);
- if (!mo_file)
- return;
- fd_mo = grub_mofile_open (mo_file);
- /* Will try adding .gz as well. */
- if (fd_mo == NULL)
- {
- grub_free (mo_file);
- mo_file = grub_xasprintf ("%s.gz", mo_file);
- if (!mo_file)
- return;
- fd_mo = grub_mofile_open (mo_file);
- }
- if (fd_mo)
- {
- grub_gettext_offsetoriginal =
- grub_gettext_get_info (GETTEXT_OFFSET_ORIGINAL);
- grub_gettext_max = grub_gettext_get_info (GETTEXT_NUMBER_OF_STRINGS);
- grub_gettext_original = grub_gettext;
- grub_gettext = grub_gettext_translate;
- }
- }
- static void
- grub_gettext_delete_list (void)
- {
- struct grub_gettext_msg *item;
- while ((item =
- grub_list_pop (GRUB_AS_LIST_P (&grub_gettext_msg_list))) != 0)
- {
- char *original = (char *) ((struct grub_gettext_msg *) item)->name;
- grub_free (original);
- /* Don't delete the translated message because could be in use. */
- }
- }
- static char *
- grub_gettext_env_write_lang (struct grub_env_var *var
- __attribute__ ((unused)), const char *val)
- {
- grub_gettext_init_ext (val);
- grub_gettext_delete_list ();
- return grub_strdup (val);
- }
- static grub_err_t
- grub_cmd_translate (grub_command_t cmd __attribute__ ((unused)),
- int argc, char **args)
- {
- if (argc != 1)
- return grub_error (GRUB_ERR_BAD_ARGUMENT, "text to translate required");
- const char *translation;
- translation = grub_gettext_translate (args[0]);
- grub_printf ("%s\n", translation);
- return 0;
- }
- GRUB_MOD_INIT (gettext)
- {
- (void) mod; /* To stop warning. */
- const char *lang;
- lang = grub_env_get ("lang");
- grub_gettext_init_ext (lang);
- grub_register_command_p1 ("gettext", grub_cmd_translate,
- N_("STRING"),
- N_("Translates the string with the current settings."));
- /* Reload .mo file information if lang changes. */
- grub_register_variable_hook ("lang", NULL, grub_gettext_env_write_lang);
- /* Preserve hooks after context changes. */
- grub_env_export ("lang");
- }
- GRUB_MOD_FINI (gettext)
- {
- if (fd_mo != 0)
- grub_file_close (fd_mo);
- grub_gettext_delete_list ();
- grub_gettext = grub_gettext_original;
- }
|