* 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>
static grub_file_t fd_mo;
-static int grub_gettext_offsetoriginal;
-static int grub_gettext_max;
+static grub_off_t grub_gettext_offset_original;
+static grub_off_t grub_gettext_offset_translation;
+static grub_size_t grub_gettext_max;
+static int grub_gettext_max_log;
static const char *(*grub_gettext_original) (const char *s);
struct grub_gettext_msg
{
- struct grub_gettext_msg *next;
- struct grub_gettext_msg *prev;
- const char *name;
-
- const char *translated;
+ char *name;
+ char *translated;
};
static 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
+struct header
+{
+ grub_uint32_t magic;
+ grub_uint32_t version;
+ grub_uint32_t number_of_strings;
+ grub_uint32_t offset_original;
+ grub_uint32_t offset_translation;
+};
+
+struct string_descriptor
+{
+ grub_uint32_t length;
+ grub_uint32_t offset;
+};
#define MO_MAGIC_NUMBER 0x950412de
-static grub_ssize_t
+static grub_err_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 grub_errno;
+ if (grub_file_read (file, buf, len) != (grub_ssize_t) len)
{
- return -1;
+ if (!grub_errno)
+ grub_error (GRUB_ERR_READ_ERROR, N_("premature end of file"));
+ return grub_errno;
}
- return grub_file_read (file, buf, len);
+ return GRUB_ERR_NONE;
}
-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)
+static char *
+grub_gettext_getstr_from_position (grub_off_t off,
+ grub_size_t position)
{
- int offsettranslation;
- int internal_position;
- grub_uint32_t length, offset;
+ grub_off_t internal_position;
+ grub_size_t length;
+ grub_off_t offset;
char *translation;
+ struct string_descriptor desc;
+ grub_err_t err;
- offsettranslation = grub_gettext_get_info (GETTEXT_OFFSET_TRANSLATION);
+ internal_position = (off + position * sizeof (desc));
- 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);
+ err = grub_gettext_pread (fd_mo, (char *) &desc,
+ sizeof (desc), internal_position);
+ if (err)
+ return NULL;
+ length = grub_cpu_to_le32 (desc.length);
+ offset = grub_cpu_to_le32 (desc.offset);
translation = grub_malloc (length + 1);
- grub_gettext_getstring_from_offset (offset, length, translation);
+ if (!translation)
+ return NULL;
+
+ err = grub_gettext_pread (fd_mo, translation, length, offset);
+ if (err)
+ {
+ grub_free (translation);
+ return NULL;
+ }
+ translation[length] = '\0';
return translation;
}
-static char *
-grub_gettext_getstring_from_position (int position)
+static const char *
+grub_gettext_gettranslation_from_position (grub_size_t 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);
+ if (!grub_gettext_msg_list[position].translated)
+ grub_gettext_msg_list[position].translated
+ = grub_gettext_getstr_from_position (grub_gettext_offset_translation,
+ position);
+ return grub_gettext_msg_list[position].translated;
+}
- return original;
+static const char *
+grub_gettext_getstring_from_position (grub_size_t position)
+{
+ if (!grub_gettext_msg_list[position].name)
+ grub_gettext_msg_list[position].name
+ = grub_gettext_getstr_from_position (grub_gettext_offset_original,
+ position);
+ return grub_gettext_msg_list[position].name;
}
static const char *
grub_gettext_translate (const char *orig)
{
- char *current_string;
- const char *ret;
+ grub_size_t current = 0;
+ int i;
+ const char *current_string;
+ static int depth = 0;
- int min, max, current;
- int found = 0;
+ if (!grub_gettext_msg_list || !fd_mo)
+ return orig;
- struct grub_gettext_msg *cur;
+ /* Shouldn't happen. Just a precaution if our own code
+ calls gettext somehow. */
+ if (depth > 2)
+ return orig;
+ depth++;
/* 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)
+ for (i = grub_gettext_max_log; i >= 0; i--)
{
- grub_error_pop ();
- return cur->translated;
- }
+ grub_size_t test;
+ int cmp;
- if (fd_mo == 0)
- {
- grub_error_pop ();
- return orig;
- }
+ test = current | (1 << i);
+ if (test >= grub_gettext_max)
+ continue;
- min = 0;
- max = grub_gettext_max;
+ current_string = grub_gettext_getstring_from_position (test);
- 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)
+ if (!current_string)
{
- grub_free (current_string);
- max = current;
+ grub_errno = GRUB_ERR_NONE;
+ grub_error_pop ();
+ depth--;
+ return orig;
}
- 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)
+ /* Search by bisection. */
+ cmp = grub_strcmp (current_string, orig);
+ if (cmp <= 0)
+ current = test;
+ if (cmp == 0)
{
- cur->name = grub_strdup (orig);
- if (cur->name)
+ const char *ret = 0;
+ ret = grub_gettext_gettranslation_from_position (current);
+ if (!ret)
{
- cur->translated = ret;
- grub_list_push (GRUB_AS_LIST_P (&grub_gettext_msg_list),
- GRUB_AS_LIST (cur));
+ grub_errno = GRUB_ERR_NONE;
+ grub_error_pop ();
+ depth--;
+ return orig;
}
+ grub_error_pop ();
+ depth--;
+ return ret;
}
- else
- grub_errno = GRUB_ERR_NONE;
}
grub_error_pop ();
- return ret;
+ depth--;
+ return orig;
+}
+
+static void
+grub_gettext_delete_list (void)
+{
+ struct grub_gettext_msg *l = grub_gettext_msg_list;
+ grub_size_t i;
+
+ if (!l)
+ return;
+ grub_gettext_msg_list = 0;
+ for (i = 0; i < grub_gettext_max; i++)
+ grub_free (l[i].name);
+ /* Don't delete the translated message because could be in use. */
+ grub_free (l);
}
/* This is similar to grub_file_open. */
-static grub_file_t
+static grub_err_t
grub_mofile_open (const char *filename)
{
- int unsigned magic;
- int version;
+ struct header head;
+ grub_err_t err;
+ grub_file_t fd;
/* Using fd_mo and not another variable because
it's needed for grub_gettext_get_info. */
- fd_mo = grub_file_open (filename);
- grub_errno = GRUB_ERR_NONE;
+ fd = grub_file_open (filename);
+
+ if (!fd)
+ return grub_errno;
- if (!fd_mo)
+ err = grub_gettext_pread (fd, &head, sizeof (head), 0);
+ if (err)
{
- grub_dprintf ("gettext", "Cannot read %s\n", filename);
- return 0;
+ grub_file_close (fd);
+ return err;
}
- magic = grub_gettext_get_info (GETTEXT_MAGIC_NUMBER);
+ if (head.magic != grub_cpu_to_le32_compile_time (MO_MAGIC_NUMBER))
+ {
+ grub_file_close (fd);
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "mo: invalid mo magic in file: %s", filename);
+ }
- if (magic != MO_MAGIC_NUMBER)
+ if (head.version != 0)
{
- grub_error (GRUB_ERR_BAD_FILE_TYPE, "mo: invalid mo file: %s",
- filename);
- grub_file_close (fd_mo);
- fd_mo = 0;
- return 0;
+ grub_file_close (fd);
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "mo: invalid mo version in file: %s", filename);
}
- version = grub_gettext_get_info (GETTEXT_FILE_FORMAT);
+ grub_gettext_offset_original = grub_le_to_cpu32 (head.offset_original);
+ grub_gettext_offset_translation = grub_le_to_cpu32 (head.offset_translation);
+ grub_gettext_max = grub_le_to_cpu32 (head.number_of_strings);
+ for (grub_gettext_max_log = 0; grub_gettext_max >> grub_gettext_max_log;
+ grub_gettext_max_log++);
+ if (fd_mo)
+ grub_file_close (fd_mo);
+ fd_mo = 0;
- if (version != 0)
+ grub_gettext_delete_list ();
+ grub_gettext_msg_list = grub_zalloc (grub_gettext_max
+ * sizeof (grub_gettext_msg_list[0]));
+ if (!grub_gettext_msg_list)
{
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "mo: invalid mo version in file: %s", filename);
- fd_mo = 0;
- return 0;
+ grub_file_close (fd);
+ return grub_errno;
}
-
- return fd_mo;
+ fd_mo = fd;
+ if (grub_gettext != grub_gettext_translate)
+ {
+ grub_gettext_original = grub_gettext;
+ grub_gettext = grub_gettext_translate;
+ }
+ return 0;
}
/* Returning grub_file_t would be more natural, but grub_mofile_open assigns
to fd_mo anyway ... */
-static void
+static grub_err_t
grub_mofile_open_lang (const char *locale_dir, const char *locale)
{
char *mo_file;
+ grub_err_t err;
/* mo_file e.g.: /boot/grub/locale/ca.mo */
mo_file = grub_xasprintf ("%s/%s.mo", locale_dir, locale);
if (!mo_file)
- return;
+ return grub_errno;
- fd_mo = grub_mofile_open (mo_file);
+ err = grub_mofile_open (mo_file);
/* Will try adding .gz as well. */
- if (fd_mo == NULL)
+ if (err)
{
char *mo_file_old;
+ grub_errno = GRUB_ERR_NONE;
mo_file_old = mo_file;
mo_file = grub_xasprintf ("%s.gz", mo_file);
grub_free (mo_file_old);
if (!mo_file)
- return;
- fd_mo = grub_mofile_open (mo_file);
+ return grub_errno;
+ err = grub_mofile_open (mo_file);
}
+ return err;
}
-static void
+static grub_err_t
grub_gettext_init_ext (const char *locale)
{
const char *locale_dir;
+ grub_err_t err;
if (!locale)
- return;
+ return 0;
locale_dir = grub_env_get ("locale_dir");
if (locale_dir == NULL)
{
grub_dprintf ("gettext", "locale_dir variable is not set up.\n");
- return;
+ return 0;
}
- fd_mo = NULL;
-
- grub_mofile_open_lang (locale_dir, locale);
+ err = grub_mofile_open_lang (locale_dir, locale);
/* ll_CC didn't work, so try ll. */
- if (fd_mo == NULL)
+ if (err)
{
char *lang = grub_strdup (locale);
- char *underscore = grub_strchr (lang, '_');
+ char *underscore = lang ? grub_strchr (lang, '_') : 0;
if (underscore)
{
*underscore = '\0';
- grub_mofile_open_lang (locale_dir, lang);
+ grub_errno = GRUB_ERR_NONE;
+ err = grub_mofile_open_lang (locale_dir, lang);
}
grub_free (lang);
}
-
- 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)
-{
- while (grub_gettext_msg_list)
- {
- grub_free ((char *) grub_gettext_msg_list->name);
- grub_gettext_msg_list = grub_gettext_msg_list->next;
- /* Don't delete the translated message because could be in use. */
- }
+ return err;
}
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 ();
+ grub_err_t err;
+ err = grub_gettext_init_ext (val);
+ if (err)
+ {
+ grub_print_error ();
+ return NULL;
+ }
return grub_strdup (val);
}