From: "Vladimir 'φ-coder/phcoder' Serbinenko" <phcoder@gmail.com>
To: The development of GRUB 2 <grub-devel@gnu.org>
Subject: gettext $"..."
Date: Sun, 11 Mar 2012 02:09:56 +0100 [thread overview]
Message-ID: <4F5BFB64.5060101@gmail.com> (raw)
[-- 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 --]
next reply other threads:[~2012-03-11 1:10 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-03-11 1:09 Vladimir 'φ-coder/phcoder' Serbinenko [this message]
2012-03-11 1:22 ` gettext $"..." Vladimir 'φ-coder/phcoder' Serbinenko
2012-03-11 1:47 ` Vladimir 'φ-coder/phcoder' Serbinenko
2012-03-11 2:45 ` Vladimir 'φ-coder/phcoder' Serbinenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4F5BFB64.5060101@gmail.com \
--to=phcoder@gmail.com \
--cc=grub-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.