]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Add variable parsing in $"..." and fix several mismatches with bash.
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 11 Mar 2012 13:46:48 +0000 (14:46 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 11 Mar 2012 13:46:48 +0000 (14:46 +0100)
* 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
Makefile.util.def
grub-core/script/execute.c
grub-core/script/yylex.l
tests/grub_script_gettext.in [new file with mode: 0644]

index 079490fda718e47fd4f5be7959c458e047bf928a..1b11461f37a36400ab945205839d3303abcde541 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2012-03-11  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       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  <phcoder@gmail.com>
 
        Fix handling of leading spaces in scripts.
index 23f2f6007be142dea5cde4ecb4f25e0af77c0b15..c41b76e0ceb7584d38e314a1392e046c9c1cbe2a 100644 (file)
@@ -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;
index 9ec135ce88a6d6cc225be2c002c7ed422be9ea78..bc82f56b7179dc99e950574fac605fd4e241216c 100644 (file)
@@ -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;
index 85314ef7064283785e7fd73a74955d6fc6e2d370..3d6899a93e89a23b4e3aa6d4a1476fbfa2a5ca86 100644 (file)
@@ -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))
 }
 
 <I18NQUOTE>{
-  \\\\          { 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 (file)
index 0000000..cc42c67
--- /dev/null
@@ -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 <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\""
+
+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"