diff --git a/ChangeLog b/ChangeLog index 17b770968..da59ff5da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2009-11-22 Carles Pina i Estany + + * conf/common.rmk: Add grub-gettext_lib target and updates + lib_DATA and CLEANFILES. Adds gettext.mod SOURCES, CFLAGS, + LDFLAGS. + * gettext/gettext.c: New file. (Reads mo files). + * include/grub/file.h (grub_file_pread): New prototype. + * include/grub/i18n.h (_): New prototype. + * include/grub/misc.h (grub_gettext_dummy, grub_gettext): New + prototypes. + * kern/misc.c (grub_gettext_dummy): New function. + * normal/menu_text.c: Include . + * normal/menu_text.c (print_timeout): Gettexttize string. + * normal/menu_text.c (print_message): Gettexttize string. + * po/POTFILES: Add `normal/menu_text.c'. + * po/ca.po: Add new translations. + * util/grub.d/00_header.in: Define locale_dir and lang. insmod + gettext module and defines locale_dir and lang in grub.cfg. + * NEWS: Add gettext support. + 2009-11-23 Robert Millan * util/hostdisk.c: Include `'. diff --git a/NEWS b/NEWS index 7c29cc948..bf7492c6d 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,7 @@ New in 1.97 - : +* Add support for gettext. + * Add support for loading XNU (MacOS X kernel). * ACPI override support. diff --git a/conf/common.rmk b/conf/common.rmk index 00d579600..532a72f67 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -150,6 +150,12 @@ update-grub_lib: util/update-grub_lib.in config.status lib_SCRIPTS += update-grub_lib CLEANFILES += update-grub_lib +grub-gettext_lib: util/grub-gettext_lib.in config.status + ./config.status --file=$@:$< + chmod +x $@ +lib_DATA += grub-gettext_lib +CLEANFILES += grub-gettext_lib + %: util/grub.d/%.in config.status ./config.status --file=$@:$< chmod +x $@ @@ -597,6 +603,12 @@ bufio_mod_SOURCES = io/bufio.c bufio_mod_CFLAGS = $(COMMON_CFLAGS) bufio_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For gettext.mod. +pkglib_MODULES += gettext.mod +gettext_mod_SOURCES = gettext/gettext.c +gettext_mod_CFLAGS = $(COMMON_CFLAGS) +gettext_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Misc. pkglib_MODULES += xnu_uuid.mod diff --git a/gettext/gettext.c b/gettext/gettext.c new file mode 100644 index 000000000..5c1279dc3 --- /dev/null +++ b/gettext/gettext.c @@ -0,0 +1,310 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + .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); + +#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 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; + char *ret; + + int min, max, current; + + if (fd_mo == 0) + return orig; + + min = 0; + max = grub_gettext_max; + + current = (max + min) / 2; + + while (current != min && current != max) + { + 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); + return grub_gettext_gettranslation_from_position (current); + } + current = (max + min) / 2; + } + + ret = grub_malloc (grub_strlen (orig) + 1); + grub_strcpy (ret, orig); + 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\n", 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_printf ("locale_dir variable is not set up."); + return; + } + + fd_mo = NULL; + + /* mo_file e.g.: /boot/grub/locale/ca.mo */ + + mo_file = + grub_malloc (grub_strlen (locale_dir) + grub_strlen ("/") + + grub_strlen (lang) + grub_strlen (".mo") + 1); + + /* Warning: if changing some paths in the below line, change the grub_malloc + contents below. */ + + grub_sprintf (mo_file, "%s/%s.mo", locale_dir, lang); + + fd_mo = grub_mofile_open (mo_file); + + /* Will try adding .gz as well. */ + if (fd_mo == NULL) + { + grub_sprintf (mo_file, "%s.gz", mo_file); + 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 char * +grub_gettext_env_write_lang (struct grub_env_var *var + __attribute__ ((unused)), const char *val) +{ + grub_gettext_init_ext (val); + + 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, + "gettext STRING", + "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 = grub_gettext_original; +} diff --git a/include/grub/i18n.h b/include/grub/i18n.h index 99efb819a..e4e06e9cd 100644 --- a/include/grub/i18n.h +++ b/include/grub/i18n.h @@ -24,7 +24,7 @@ # include # define _(str) gettext(str) #else -# define _(str) str +# define _(str) grub_gettext(str) #endif #define N_(str) str diff --git a/include/grub/misc.h b/include/grub/misc.h index faa2d5c42..8ec8ad822 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -1,7 +1,7 @@ /* misc.h - prototypes for misc functions */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2005,2006,2007,2008,2009,2008 Free Software Foundation, Inc. + * Copyright (C) 2002,2003,2005,2006,2007,2008,2009,2008,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 @@ -191,6 +191,9 @@ grub_ssize_t EXPORT_FUNC(grub_utf8_to_ucs4) (grub_uint32_t *dest, grub_uint64_t EXPORT_FUNC(grub_divmod64) (grub_uint64_t n, grub_uint32_t d, grub_uint32_t *r); +const char *EXPORT_FUNC(grub_gettext_dummy) (const char *s); +extern const char *(*EXPORT_VAR(grub_gettext)) (const char *s); + #ifdef NEED_ENABLE_EXECUTE_STACK void EXPORT_FUNC(__enable_execute_stack) (void *addr); #endif diff --git a/kern/misc.c b/kern/misc.c index cacfbc753..827f864c4 100644 --- a/kern/misc.c +++ b/kern/misc.c @@ -30,6 +30,8 @@ grub_iswordseparator (int c) return (grub_isspace (c) || c == ',' || c == ';' || c == '|' || c == '&'); } +const char* (*grub_gettext) (const char *s) = grub_gettext_dummy; + void * grub_memmove (void *dest, const void *src, grub_size_t n) { @@ -984,6 +986,13 @@ grub_utf8_to_ucs4 (grub_uint32_t *dest, grub_size_t destsize, return p - dest; } +/* grub_gettext_dummy is not translating anything. */ +const char * +grub_gettext_dummy (const char *s) +{ + return s; +} + /* Abort GRUB. This function does not return. */ void grub_abort (void) diff --git a/normal/main.c b/normal/main.c index 2f19542e8..784c72449 100644 --- a/normal/main.c +++ b/normal/main.c @@ -29,6 +29,7 @@ #include #include #include +#include #define GRUB_DEFAULT_HISTORY_SIZE 50 @@ -508,10 +509,10 @@ grub_normal_reader_init (void) grub_normal_init_page (); grub_setcursor (1); - grub_printf ("\ + grub_printf (_("\ [ Minimal BASH-like line editing is supported. For the first word, TAB\n\ lists possible command completions. Anywhere else TAB lists possible\n\ - device/file completions.%s ]\n\n", + device/file completions.%s ]\n\n"), reader_nested ? " ESC at any time exits." : ""); return 0; diff --git a/normal/menu_entry.c b/normal/menu_entry.c index 75a63779f..7478c33ae 100644 --- a/normal/menu_entry.c +++ b/normal/menu_entry.c @@ -24,6 +24,7 @@ #include #include #include +#include enum update_mode { @@ -996,7 +997,7 @@ run (struct screen *screen) } grub_cls (); - grub_printf (" Booting a command list\n\n"); + grub_printf (_(" Booting a command list\n\n")); /* Execute the script, line for line. */ @@ -1176,6 +1177,6 @@ grub_menu_entry_run (grub_menu_entry_t entry) grub_cls (); grub_print_error (); grub_errno = GRUB_ERR_NONE; - grub_printf ("\nPress any key to continue..."); + grub_printf (_("\nPress any key to continue...")); (void) grub_getkey (); } diff --git a/normal/menu_text.c b/normal/menu_text.c index e0d96c47f..4ff22e928 100644 --- a/normal/menu_text.c +++ b/normal/menu_text.c @@ -25,6 +25,7 @@ #include #include #include +#include /* Time to delay after displaying an error message about a default/fallback entry failing to boot. */ @@ -93,8 +94,8 @@ print_message (int nested, int edit) } else { - grub_printf ("\n\ - Use the %C and %C keys to select which entry is highlighted.\n", + grub_printf (_("\n\ + Use the %C and %C keys to select which entry is highlighted.\n"), (grub_uint32_t) GRUB_TERM_DISP_UP, (grub_uint32_t) GRUB_TERM_DISP_DOWN); grub_printf ("\ Press enter to boot the selected OS, \'e\' to edit the\n\ diff --git a/po/POTFILES b/po/POTFILES index 5b1239a5e..c0fc40c30 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -10,3 +10,5 @@ util/mkisofs/multi.c util/mkisofs/rock.c util/mkisofs/tree.c util/mkisofs/write.c + +normal/menu_text.c diff --git a/po/ca.po b/po/ca.po index a66c49f2e..58783edbd 100644 --- a/po/ca.po +++ b/po/ca.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: GNU GRUB\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-11-23 14:14+0100\n" +"POT-Creation-Date: 2009-11-22 11:45+0000\n" "PO-Revision-Date: 2009-11-17 12:26+0100\n" "Last-Translator: Robert Millan \n" "Language-Team: None \n" @@ -996,6 +996,15 @@ msgstr "" msgid "Path table size(bytes): %d\n" msgstr "" +#: normal/menu_text.c:97 +#, c-format +msgid "" +"\n" +" Use the %C and %C keys to select which entry is highlighted.\n" +msgstr "" +"\n" +" Utilitzeu les tecles %C i %C per a seleccionar l'entrada ressaltada.\n" + #: util/grub.d/10_kfreebsd.in:40 msgid "%s, with kFreeBSD %s" msgstr "" diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 9f421dc1c..96352cdae 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -22,6 +22,8 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ grub_prefix=`echo /boot/grub | sed ${transform}` +locale_dir=`echo /boot/grub/locale | sed ${transform}` +grub_lang=`echo $LANG | cut -d _ -f 1` . ${libdir}/grub/grub-mkconfig_lib @@ -100,6 +102,15 @@ EOF ;; esac +# Gettext variables and module +if [ "x${LANG}" != "xC" ] ; then + cat << EOF +set locale_dir=${locale_dir} +set lang=${grub_lang} +insmod gettext +EOF +fi + if [ "x${GRUB_HIDDEN_TIMEOUT}" != "x" ] ; then if [ "x${GRUB_HIDDEN_TIMEOUT_QUIET}" = "xtrue" ] ; then verbose=