From 546fbe9b5abc2a99ed277617cb88e8908e68db5f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 11 Mar 2012 14:46:48 +0100 Subject: [PATCH] Add variable parsing in $"..." and fix several mismatches with bash. * Makefile.util.def (grub_script_gettext): New test. * grub-core/script/execute.c (parse_string): New function. (gettext_append): Likewise. (grub_script_arglist_to_argv): Use gettext_append. * grub-core/script/yylex.l: Fix slash and newline handling in $"...". * tests/grub_script_gettext.in: New file. --- ChangeLog | 11 ++ Makefile.util.def | 6 + grub-core/script/execute.c | 211 ++++++++++++++++++++++++++++++++++- grub-core/script/yylex.l | 5 +- tests/grub_script_gettext.in | 69 ++++++++++++ 5 files changed, 298 insertions(+), 4 deletions(-) create mode 100644 tests/grub_script_gettext.in diff --git a/ChangeLog b/ChangeLog index 079490fda..1b11461f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2012-03-11 Vladimir Serbinenko + + Add variable parsing in $"..." and fix several mismatches with bash. + + * Makefile.util.def (grub_script_gettext): New test. + * grub-core/script/execute.c (parse_string): New function. + (gettext_append): Likewise. + (grub_script_arglist_to_argv): Use gettext_append. + * grub-core/script/yylex.l: Fix slash and newline handling in $"...". + * tests/grub_script_gettext.in: New file. + 2012-03-11 Vladimir Serbinenko Fix handling of leading spaces in scripts. diff --git a/Makefile.util.def b/Makefile.util.def index 23f2f6007..c41b76e0c 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -666,6 +666,12 @@ script = { common = tests/grub_cmd_echo.in; }; +script = { + testcase; + name = grub_script_gettext; + common = tests/grub_script_gettext.in; +}; + program = { testcase; name = example_unit_test; diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index 9ec135ce8..bc82f56b7 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -321,6 +321,214 @@ grub_script_env_set (const char *name, const char *val) 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; + default: + if (put) + *((*put)++) = '$'; + } + 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 +614,7 @@ grub_script_arglist_to_argv (struct grub_script_arglist *arglist, 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; diff --git a/grub-core/script/yylex.l b/grub-core/script/yylex.l index 85314ef70..3d6899a93 100644 --- a/grub-core/script/yylex.l +++ b/grub-core/script/yylex.l @@ -139,7 +139,7 @@ SPECIAL \?|\#|\*|\@ VARIABLE ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|${SPECIAL}|$\{{SPECIAL}\} WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE}|{I18NSTR})+ -MULTILINE {WORD}?((\"{DQCHR}*)|(\'{SQCHR}*)|(\\\n)) +MULTILINE {WORD}?((\"{DQCHR}*)|(\$\"{DQCHR}*)|(\'{SQCHR}*)|(\\\n)) %x SPLIT %x DQUOTE @@ -289,7 +289,7 @@ MULTILINE {WORD}?((\"{DQCHR}*)|(\'{SQCHR}*)|(\\\n)) } { - \\\\ { COPY ("\\", 1); } + \\\\ { COPY ("\\\\", 2); } \\\" { COPY ("\"", 1); } \\\n { /* ignore */ } [^\"\\\n]+ { COPY (yytext, yyleng); } @@ -297,6 +297,7 @@ MULTILINE {WORD}?((\"{DQCHR}*)|(\'{SQCHR}*)|(\\\n)) yy_pop_state (yyscanner); ARG (GRUB_SCRIPT_ARG_TYPE_GETTEXT); } + \\ { COPY ("\\", 1); } (.|\n) { COPY (yytext, yyleng); } } diff --git a/tests/grub_script_gettext.in b/tests/grub_script_gettext.in new file mode 100644 index 000000000..cc42c6751 --- /dev/null +++ b/tests/grub_script_gettext.in @@ -0,0 +1,69 @@ +#! @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 . + +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\"" + +echo $"$" +echo $"$,x" + +echo $"one +" +echo $"one +\"" +echo $"one +two" + +echo one$"two +"three +echo one$"two +\""three +echo one$"two +\"three\" +four" + +echo $"one\ +" +echo $"one\ +\"" +echo $"one\ +two" + +echo one$"two\ +"three +echo one$"two\ +\""three +echo one$"two\ +\"three\"\ +four" + +if test -n "$grubshell"; then insmod regexp; fi + +echo $"*" + +foo="*" +echo $"$foo"