You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2022/03/08 18:57:37 UTC

[incubator-nuttx] 02/02: libc/gettext: Support the plural format

This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 01517b2ebe6c073724663974562993eb8aea46a9
Author: Xiang Xiao <xi...@xiaomi.com>
AuthorDate: Tue Mar 8 03:33:31 2022 +0800

    libc/gettext: Support the plural format
    
    Signed-off-by: Xiang Xiao <xi...@xiaomi.com>
---
 libs/libc/locale/lib_gettext.c | 352 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 351 insertions(+), 1 deletion(-)

diff --git a/libs/libc/locale/lib_gettext.c b/libs/libc/locale/lib_gettext.c
index 2882ba1..2c6bef6 100644
--- a/libs/libc/locale/lib_gettext.c
+++ b/libs/libc/locale/lib_gettext.c
@@ -24,6 +24,7 @@
 
 #include <nuttx/config.h>
 
+#include <ctype.h>
 #include <endian.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -54,10 +55,19 @@ struct mofile_s
 {
   FAR struct mofile_s *next;
   char path[PATH_MAX];
+  FAR const char *plural_rule;
+  unsigned long nplurals;
   FAR void *map;
   size_t size;
 };
 
+struct eval_s
+{
+  unsigned long r;
+  unsigned long n;
+  int op;
+};
+
 /****************************************************************************
  * Private Data
  ****************************************************************************/
@@ -81,6 +91,13 @@ static FAR char g_domain[NAME_MAX];
 #endif
 
 /****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static FAR const char *evalexpr(FAR struct eval_s *ev,
+                                FAR const char *s, int d);
+
+/****************************************************************************
  * Private Functions
  ****************************************************************************/
 
@@ -196,6 +213,272 @@ static FAR char *molookup(FAR char *p, size_t size, FAR const char *s)
   return NULL;
 }
 
+static FAR const char *skipspace(FAR const char *s)
+{
+  while (isspace(*s))
+    {
+      s++;
+    }
+
+  return s;
+}
+
+/* Grammar:
+ *
+ * Start = Expr ';'
+ * Expr  = Or | Or '?' Expr ':' Expr
+ * Or    = And | Or '||' And
+ * And   = Eq | And '&&' Eq
+ * Eq    = Rel | Eq '==' Rel | Eq '!=' Rel
+ * Rel   = Add | Rel '<=' Add | Rel '>=' Add | Rel '<' Add | Rel '>' Add
+ * Add   = Mul | Add '+' Mul | Add '-' Mul
+ * Mul   = Prim | Mul '*' Prim | Mul '/' Prim | Mul '%' Prim
+ * Prim  = '(' Expr ')' | '!' Prim | decimal | 'n'
+ *
+ * Internals:
+ *
+ * Recursive descent expression evaluator with stack depth limit.
+ * for binary operators an operator-precedence parser is used.
+ * eval* functions store the result of the parsed subexpression
+ * and return a pointer to the next non-space character.
+ */
+
+static FAR const char *evalprim(FAR struct eval_s *ev,
+                                FAR const char *s, int d)
+{
+  FAR char *e;
+
+  if (--d < 0)
+    {
+      return "";
+    }
+
+  s = skipspace(s);
+  if (isdigit(*s))
+    {
+      ev->r = strtoul(s, &e, 10);
+      if (e == s || ev->r == -1)
+        {
+          return "";
+        }
+
+      return skipspace(e);
+    }
+
+  if (*s == 'n')
+    {
+      ev->r = ev->n;
+      return skipspace(s + 1);
+    }
+
+  if (*s == '(')
+    {
+      s = evalexpr(ev, s + 1, d);
+      if (*s != ')')
+        {
+          return "";
+        }
+
+      return skipspace(s + 1);
+    }
+
+  if (*s == '!')
+    {
+      s = evalprim(ev, s + 1, d);
+      ev->r = !ev->r;
+      return s;
+    }
+
+  return "";
+}
+
+static bool binop(FAR struct eval_s *ev, int op, unsigned long left)
+{
+  unsigned long a = left;
+  unsigned long b = ev->r;
+
+  switch (op)
+    {
+      case 0:
+        ev->r = a || b;
+        return true;
+
+      case 1:
+        ev->r = a && b;
+        return true;
+
+      case 2:
+        ev->r = a == b;
+        return true;
+
+      case 3:
+        ev->r = a != b;
+        return true;
+
+      case 4:
+        ev->r = a >= b;
+        return true;
+
+      case 5:
+        ev->r = a <= b;
+        return true;
+
+      case 6:
+        ev->r = a > b;
+        return true;
+
+      case 7:
+        ev->r = a < b;
+        return true;
+
+      case 8:
+        ev->r = a + b;
+        return true;
+
+      case 9:
+        ev->r = a - b;
+        return true;
+
+      case 10:
+        ev->r = a * b;
+        return true;
+
+      case 11:
+        if (b)
+          {
+            ev->r = a % b;
+            return true;
+          }
+
+        return false;
+
+      case 12:
+        if (b)
+          {
+            ev->r = a / b;
+            return true;
+          }
+
+        return false;
+    }
+
+  return false;
+}
+
+static FAR const char *parseop(FAR struct eval_s *ev, FAR const char *s)
+{
+  static const char opch[] =
+  {
+    '|', '&', '=', '!', '>', '<', '+', '-', '*', '%', '/'
+  };
+
+  static const char opch2[] =
+  {
+    '|', '&', '=', '=', '=', '='
+  };
+
+  int i;
+
+  for (i = 0; i < sizeof(opch); i++)
+    {
+      if (*s == opch[i])
+        {
+          /* note: >,< are accepted with or without = */
+
+          if (i < sizeof(opch2) && *(s + 1) == opch2[i])
+            {
+              ev->op = i;
+              return s + 2;
+            }
+
+          if (i >= 4)
+            {
+              ev->op = i + 2;
+              return s + 1;
+            }
+
+          break;
+        }
+    }
+
+  ev->op = 13;
+  return s;
+}
+
+static FAR const char *evalbinop(FAR struct eval_s *ev,
+                                 FAR const char *s, int minprec, int d)
+{
+  static const char prec[14] =
+  {
+    1, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 0
+  };
+
+  unsigned long left;
+  int op;
+
+  s = evalprim(ev, s, --d);
+  s = parseop(ev, s);
+
+  for (; ; )
+    {
+      /* ev->r (left hand side value) and ev->op are now set,
+       * get the right hand side or back out if op has low prec,
+       * if op was missing then prec[op]==0
+       */
+
+      op = ev->op;
+      if (prec[op] <= minprec)
+        {
+          return s;
+        }
+
+      left = ev->r;
+      s = evalbinop(ev, s, prec[op], d);
+      if (!binop(ev, op, left))
+        {
+          return "";
+        }
+    }
+}
+
+static FAR const char *evalexpr(FAR struct eval_s *ev,
+                                FAR const char *s, int d)
+{
+  unsigned long a;
+  unsigned long b;
+
+  s = evalbinop(ev, s, 0, --d);
+  if (*s != '?')
+    {
+      return s;
+    }
+
+  a = ev->r;
+  s = evalexpr(ev, s + 1, d);
+  if (*s != ':')
+    {
+      return "";
+    }
+
+  b = ev->r;
+  s = evalexpr(ev, s + 1, d);
+  ev->r = a ? b : ev->r;
+
+  return s;
+}
+
+unsigned long eval(FAR const char *s, unsigned long n)
+{
+  struct eval_s ev;
+
+  memset(&ev, 0, sizeof(ev));
+
+  ev.n = n;
+  s = evalexpr(&ev, s, 100);
+
+  return *s == ';' ? ev.r : -1;
+}
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -306,6 +589,8 @@ FAR char *dcngettext(FAR const char *domainname,
 
   if (mofile == NULL)
     {
+      FAR const char *r;
+
       mofile = lib_malloc(sizeof(*mofile));
       if (mofile == NULL)
         {
@@ -322,6 +607,42 @@ FAR char *dcngettext(FAR const char *domainname,
           return notrans;
         }
 
+      /* Initialize the default plural rule */
+
+      mofile->plural_rule = "n!=1;";
+      mofile->nplurals = 2;
+
+      /* Parse the plural rule from the header entry(empty string) */
+
+      r = molookup(mofile->map, mofile->size, "");
+      while (r != NULL && strncmp(r, "Plural-Forms:", 13))
+        {
+          r = strchr(r, '\n');
+          if (r != NULL)
+            {
+              r += 1;
+            }
+        }
+
+      if (r != NULL)
+        {
+          r = skipspace(r + 13);
+          if (strncmp(r, "nplurals=", 9) == 0)
+            {
+              mofile->nplurals = strtoul(r + 9, (FAR char **)&r, 10);
+            }
+
+          r = strchr(r, ';');
+          if (r != NULL)
+            {
+              r = skipspace(r + 1);
+              if (strncmp(r, "plural=", 7) == 0)
+                {
+                  mofile->plural_rule = r + 7;
+                }
+            }
+        }
+
       mofile->next = g_mofile;
       g_mofile = mofile;
     }
@@ -329,7 +650,36 @@ FAR char *dcngettext(FAR const char *domainname,
   _SEM_POST(&g_sem); /* Leave look before search */
 
   trans = molookup(mofile->map, mofile->size, msgid1);
-  return trans ? trans : notrans;
+  if (trans == NULL)
+    {
+      return notrans;
+    }
+
+  /* Process the plural rule if request */
+
+  if (msgid2 && mofile->nplurals)
+    {
+      unsigned long plural = eval(mofile->plural_rule, n);
+      if (plural >= mofile->nplurals)
+        {
+          return notrans;
+        }
+
+      while (plural-- != 0)
+        {
+          size_t rem = mofile->size - (trans - (FAR char *)mofile->map);
+          size_t l = strnlen(trans, rem);
+
+          if (l + 1 >= rem)
+            {
+              return notrans;
+            }
+
+          trans += l + 1;
+        }
+    }
+
+  return trans;
 }
 
 /****************************************************************************