On 11.03.2012 02:47, Vladimir 'φ-coder/phcoder' Serbinenko wrote: > Underscore handling. > On 11.03.2012 02:22, Vladimir 'φ-coder/phcoder' Serbinenko wrote: >> Update to fix escaping. >> $"<string>" as long as <string> doesn't contain $* or $@ in absence of >> gettext should behave the same as "<string>" >> On 11.03.2012 02:09, Vladimir 'φ-coder/phcoder' Serbinenko wrote: >>> Hello, all. Following discussion with Jordan Uggla it was found out that >>> current syntax of $"..." without variable expansion isn't enough for a >>> meaningful i18n and committing ourselves to it by releasing a version >>> with it would be counter-productive as later we'll need something better >>> but won't be able to remove something mostly useless. Since the bash >>> behaviour is unsafe due to variable expansion in translated strings. But >>> it's the very feature that is interesting for translation. There is >>> however a compromise by allowing in translated string only same >>> variables as in the original.This allows translator to rearrange >>> variables as he sees fit but in the same time makes it safe (basically >>> it's printf with reduced syntax and named tokens and without ability to >>> run over the end). Fortunately only betas were released with $"..." >>> feature and no project has used it for i18n yet. Attached patch >>> implements the described behaviour. >>> Variables $* and $@ can't be used inside $"...". >>> Can someone have a look, comment on approach and/or provide tests? >>> >
-- Regards Vladimir 'φ-coder/phcoder' Serbinenko
=== modified file 'Makefile.util.def'
--- Makefile.util.def 2012-03-10 20:30:37 +0000
+++ Makefile.util.def 2012-03-11 02:31:15 +0000
@@ -666,6 +666,12 @@
common = tests/grub_cmd_echo.in;
};
+script = {
+ testcase;
+ name = grub_script_gettext;
+ common = tests/grub_script_gettext.in;
+};
+
program = {
testcase;
name = example_unit_test;
=== modified file 'grub-core/script/execute.c'
--- grub-core/script/execute.c 2012-03-10 12:19:46 +0000
+++ grub-core/script/execute.c 2012-03-11 02:29:54 +0000
@@ -321,6 +321,211 @@
return grub_env_set (name, val);
}
+static int
+parse_string (const char *str,
+ int (*hook) (const char *var, grub_size_t varlen),
+ char **put)
+{
+ const char *ptr;
+ int escaped = 0;
+ const char *optr;
+
+ for (ptr = str; ptr && *ptr; )
+ switch (*ptr)
+ {
+ case '\\':
+ escaped = !escaped;
+ if (!escaped && put)
+ *((*put)++) = '\\';
+ ptr++;
+ break;
+ case '$':
+ if (escaped)
+ {
+ escaped = 0;
+ if (put)
+ *((*put)++) = *ptr;
+ ptr++;
+ break;
+ }
+
+ ptr++;
+ switch (*ptr)
+ {
+ case '{':
+ {
+ optr = ptr + 1;
+ ptr = grub_strchr (optr, '}');
+ if (!ptr)
+ break;
+ if (hook (optr, ptr - optr))
+ return 1;
+ ptr++;
+ break;
+ }
+ case '0' ... '9':
+ optr = ptr;
+ while (*ptr >= '0' && *ptr <= '9')
+ ptr++;
+ if (hook (optr, ptr - optr))
+ return 1;
+ break;
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ case '_':
+ optr = ptr;
+ while ((*ptr >= '0' && *ptr <= '9')
+ || (*ptr >= 'a' && *ptr <= 'z')
+ || (*ptr >= 'A' && *ptr <= 'Z')
+ || *ptr == '_')
+ ptr++;
+ if (hook (optr, ptr - optr))
+ return 1;
+ break;
+ case '?':
+ case '#':
+ if (hook (ptr, 1))
+ return 1;
+ ptr++;
+ break;
+ }
+ break;
+ default:
+ if (escaped && put)
+ *((*put)++) = '\\';
+ escaped = 0;
+ if (put)
+ *((*put)++) = *ptr;
+ ptr++;
+ break;
+ }
+ return 0;
+}
+
+static int
+gettext_append (struct grub_script_argv *result, const char *orig_str)
+{
+ const char *template;
+ char *res = 0, *ptr;
+ char **allowed_strings;
+ grub_size_t nallowed_strings = 0;
+ grub_size_t additional_len = 1;
+ int rval = 1;
+ const char *iptr;
+
+ auto int save_allow (const char *str, grub_size_t len);
+ int save_allow (const char *str, grub_size_t len)
+ {
+ allowed_strings[nallowed_strings++] = grub_strndup (str, len);
+ if (!allowed_strings[nallowed_strings - 1])
+ return 1;
+ return 0;
+ }
+
+ auto int getlen (const char *str, grub_size_t len);
+ int getlen (const char *str, grub_size_t len)
+ {
+ const char *var;
+ grub_size_t i;
+
+ for (i = 0; i < nallowed_strings; i++)
+ if (grub_strncmp (allowed_strings[i], str, len) == 0
+ && allowed_strings[i][len] == 0)
+ break;
+ if (i == nallowed_strings)
+ return 0;
+
+ /* Enough for any number. */
+ if (len == 1 && str[0] == '#')
+ {
+ additional_len += 30;
+ return 0;
+ }
+ var = grub_env_get (allowed_strings[i]);
+ if (var)
+ additional_len += grub_strlen (var);
+ return 0;
+ }
+
+ auto int putvar (const char *str, grub_size_t len);
+ int putvar (const char *str, grub_size_t len)
+ {
+ const char *var;
+ grub_size_t i;
+
+ for (i = 0; i < nallowed_strings; i++)
+ if (grub_strncmp (allowed_strings[i], str, len) == 0
+ && allowed_strings[i][len] == 0)
+ {
+ break;
+ }
+ if (i == nallowed_strings)
+ return 0;
+
+ /* Enough for any number. */
+ if (len == 1 && str[0] == '#')
+ {
+ grub_snprintf (ptr, 30, "%u", scope->argv.argc);
+ ptr += grub_strlen (ptr);
+ return 0;
+ }
+ var = grub_env_get (allowed_strings[i]);
+ if (var)
+ ptr = grub_stpcpy (ptr, var);
+ return 0;
+ }
+
+ grub_size_t dollar_cnt = 0;
+
+ for (iptr = orig_str; *iptr; iptr++)
+ if (*iptr == '$')
+ dollar_cnt++;
+ allowed_strings = grub_malloc (sizeof (allowed_strings[0]) * dollar_cnt);
+
+ if (parse_string (orig_str, save_allow, 0))
+ goto fail;
+
+ template = _(orig_str);
+
+ if (parse_string (template, getlen, 0))
+ goto fail;
+
+ res = grub_malloc (grub_strlen (template) + additional_len);
+ if (!res)
+ goto fail;
+ ptr = res;
+
+ if (parse_string (template, putvar, &ptr))
+ goto fail;
+
+ *ptr = 0;
+ if (grub_wildcard_translator)
+ {
+ char *escaped = 0;
+ escaped = grub_wildcard_translator->escape (res);
+ if (grub_script_argv_append (result, escaped, grub_strlen (escaped)))
+ {
+ grub_free (escaped);
+ goto fail;
+ }
+ grub_free (escaped);
+ }
+ else
+ if (grub_script_argv_append (result, res, ptr - res))
+ goto fail;
+
+ rval = 0;
+ fail:
+ grub_free (res);
+ {
+ grub_size_t i;
+ for (i = 0; i < nallowed_strings; i++)
+ grub_free (allowed_strings[i]);
+ }
+ grub_free (allowed_strings);
+ return rval;
+}
+
/* Convert arguments in ARGLIST into ARGV form. */
static int
grub_script_arglist_to_argv (struct grub_script_arglist *arglist,
@@ -406,8 +611,7 @@
case GRUB_SCRIPT_ARG_TYPE_GETTEXT:
{
- const char *t = _(arg->str);
- if (grub_script_argv_append (&result, t, grub_strlen (t)))
+ if (gettext_append (&result, arg->str))
goto fail;
}
break;
=== modified file 'grub-core/script/yylex.l'
--- grub-core/script/yylex.l 2012-02-26 18:02:46 +0000
+++ grub-core/script/yylex.l 2012-03-11 01:16:17 +0000
@@ -289,7 +289,7 @@
}
<I18NQUOTE>{
- \\\\ { COPY ("\\", 1); }
+ \\\\ { COPY ("\\\\", 2); }
\\\" { COPY ("\"", 1); }
\\\n { /* ignore */ }
[^\"\\\n]+ { COPY (yytext, yyleng); }
@@ -297,6 +297,7 @@
yy_pop_state (yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_GETTEXT);
}
+ \\ { COPY ("\\", 1); }
(.|\n) { COPY (yytext, yyleng); }
}
=== added file 'tests/grub_script_gettext.in'
--- tests/grub_script_gettext.in 1970-01-01 00:00:00 +0000
+++ tests/grub_script_gettext.in 2012-03-11 02:25:41 +0000
@@ -0,0 +1,36 @@
+#! @builddir@/grub-shell-tester
+#
+# Copyright (C) 2010,2012 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/>.
+
+echo $"foo"
+echo $"foo bar"
+
+echo -n $"foo"
+
+echo -e $"foo\nbar"
+
+echo -n -e $"foo\nbar"
+
+x=5
+echo $"$x"
+echo $"\x\\y\$x$x\\$xx${x}x\"$x\""
+
+if test -n "$grubshell"; then insmod regexp; fi
+
+echo $"*"
+
+foo="*"
+echo $"$foo"
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Grub-devel mailing list [email protected] https://lists.gnu.org/mailman/listinfo/grub-devel
