All of lore.kernel.org
 help / color / mirror / Atom feed
* gettext $"..."
@ 2012-03-11  1:09 Vladimir 'φ-coder/phcoder' Serbinenko
  2012-03-11  1:22 ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 4+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2012-03-11  1:09 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 1127 bytes --]

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


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: gettext.diff --]
[-- Type: text/x-diff; name="gettext.diff", Size: 4710 bytes --]

=== 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 00:29:19 +0000
@@ -321,6 +321,187 @@
   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)
+	  {
+	    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++;
+		if (hook (optr, ptr - optr))
+		  return 1;
+		break;
+	      case '?':
+	      case '#':
+		if (hook (ptr, 1))
+		  return 1;
+		ptr++;
+		break;
+	      }
+	  }
+      default:
+	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;
+
+  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 +587,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;


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 294 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-03-11  2:45 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-03-11  1:09 gettext $"..." Vladimir 'φ-coder/phcoder' Serbinenko
2012-03-11  1:22 ` Vladimir 'φ-coder/phcoder' Serbinenko
2012-03-11  1:47   ` Vladimir 'φ-coder/phcoder' Serbinenko
2012-03-11  2:45     ` Vladimir 'φ-coder/phcoder' Serbinenko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.