From e6bbba45110911e5c5f7e7b619e320f374f671a0 Mon Sep 17 00:00:00 2001
From: Michele Locati <michele@locati.it>
Date: Tue, 12 Jul 2016 10:11:10 +0200
Subject: [PATCH] Allow CLDR plural rules lookup fallback

Let's allow the CLDR plural rules lookup to fallback to "parent" locale ids.
For instance, looking for "it_IT.UTF-8" will also find "it_IT" and "it"
---
 gettext-tools/src/cldr-plurals.c | 130 ++++++++++++++++++++++++---------------
 1 file changed, 80 insertions(+), 50 deletions(-)

diff --git a/gettext-tools/src/cldr-plurals.c b/gettext-tools/src/cldr-plurals.c
index 56c4581..c86bd41 100644
--- a/gettext-tools/src/cldr-plurals.c
+++ b/gettext-tools/src/cldr-plurals.c
@@ -46,8 +46,9 @@ extract_rules (FILE *fp,
                const char *locale)
 {
   xmlDocPtr doc;
-  xmlNodePtr node, n;
-  size_t locale_length;
+  xmlNodePtr rootNode, pluralsNode, pluralRulesNode, n;
+  size_t locale_lengths[4];
+  size_t locale_length, locale_lengths_count, locale_lengths_index;
   char *buffer = NULL, *p;
   size_t bufmax = 0;
   size_t buflen = 0;
@@ -60,97 +61,126 @@ extract_rules (FILE *fp,
   if (doc == NULL)
     error (EXIT_FAILURE, 0, _("memory exhausted"));
 
-  node = xmlDocGetRootElement (doc);
-  if (!node || !xmlStrEqual (node->name, BAD_CAST "supplementalData"))
+  rootNode = xmlDocGetRootElement (doc);
+  if (!rootNode || !xmlStrEqual (rootNode->name, BAD_CAST "supplementalData"))
     {
       error_at_line (0, 0,
                      logical_filename,
-                     xmlGetLineNo (node),
+                     xmlGetLineNo (rootNode),
                      _("\
 The root element must be <%s>"),
                      "supplementalData");
       goto out;
     }
 
-  for (n = node->children; n; n = n->next)
+  for (pluralsNode = NULL, n = rootNode->children; n; n = n->next)
     {
       if (n->type == XML_ELEMENT_NODE
           && xmlStrEqual (n->name, BAD_CAST "plurals"))
-        break;
+        {
+          pluralsNode = n;
+          break;
+        }
     }
-  if (!n)
+  if (!pluralsNode)
     {
       error (0, 0, _("The element <%s> does not contain a <%s> element"),
              "supplementalData", "plurals");
       goto out;
     }
 
-  locale_length = strlen (locale);
-  for (n = n->children; n; n = n->next)
+  locale_lengths[0] = strlen (locale);
+  locale_lengths_count = 1;
+  for (
+    locale_lengths_index = locale_lengths[0] - 1;
+    locale_lengths_index > 0 && locale_lengths_count < 4;
+    locale_lengths_index--
+  )
+    {
+      switch (locale[locale_lengths_index])
+      {
+        case '.':
+        case '_':
+        case '-':
+        case '@':
+          locale_lengths[locale_lengths_count++] = locale_lengths_index;
+          break;
+      }
+    }
+  for (
+    pluralRulesNode = NULL, locale_lengths_index = 0;
+    locale_lengths_index < locale_lengths_count;
+    locale_lengths_index++
+  )
     {
-      xmlChar *locales;
       xmlChar *cp;
-      xmlNodePtr n2;
-      bool found = false;
-
-      if (n->type != XML_ELEMENT_NODE
-          || !xmlStrEqual (n->name, BAD_CAST "pluralRules"))
-        continue;
-
-      if (!xmlHasProp (n, BAD_CAST "locales"))
+      xmlChar *locales;
+      locale_length = locale_lengths[locale_lengths_index];
+      for (n = pluralsNode->children; n; n = n->next)
         {
-          error_at_line (0, 0,
-                         logical_filename,
-                         xmlGetLineNo (n),
-                         _("\
-The element <%s> does not have attribute <%s>"),
-                         "pluralRules", "locales");
-          continue;
-        }
+          if (n->type != XML_ELEMENT_NODE
+              || !xmlStrEqual (n->name, BAD_CAST "pluralRules"))
+            continue;
 
-      cp = locales = xmlGetProp (n, BAD_CAST "locales");
-      while (*cp != '\0')
-        {
-          while (c_isspace (*cp))
-            cp++;
-          if (xmlStrncmp (cp, BAD_CAST locale, locale_length) == 0
-              && (*(cp + locale_length) == '\0'
-                  || c_isspace (*(cp + locale_length))))
+          if (!xmlHasProp (n, BAD_CAST "locales"))
             {
-              found = true;
-              break;
+              error_at_line (0, 0,
+                             logical_filename,
+                             xmlGetLineNo (n),
+                             _("\
+  The element <%s> does not have attribute <%s>"),
+                             "pluralRules", "locales");
+              continue;
             }
-          while (*cp && !c_isspace (*cp))
-            cp++;
-        }
-      xmlFree (locales);
 
-      if (!found)
-        continue;
+          cp = locales = xmlGetProp (n, BAD_CAST "locales");
+          while (*cp != '\0')
+            {
+              while (c_isspace (*cp))
+                cp++;
+              if (xmlStrncmp (cp, BAD_CAST locale, locale_length) == 0
+                  && (*(cp + locale_length) == '\0'
+                      || c_isspace (*(cp + locale_length))))
+                {
+                  pluralRulesNode = n;
+                  break;
+                }
+              while (*cp && !c_isspace (*cp))
+                cp++;
+            }
+          xmlFree (locales);
 
-      for (n2 = n->children; n2; n2 = n2->next)
+          if (pluralRulesNode)
+            break;
+        }
+      if (pluralRulesNode)
+        break;
+    }
+  if (pluralRulesNode)
+    {
+      for (n = pluralRulesNode->children; n; n = n->next)
         {
           xmlChar *count;
           xmlChar *content;
           size_t length;
 
-          if (n2->type != XML_ELEMENT_NODE
-              || !xmlStrEqual (n2->name, BAD_CAST "pluralRule"))
+          if (n->type != XML_ELEMENT_NODE
+              || !xmlStrEqual (n->name, BAD_CAST "pluralRule"))
             continue;
 
-          if (!xmlHasProp (n2, BAD_CAST "count"))
+          if (!xmlHasProp (n, BAD_CAST "count"))
             {
               error_at_line (0, 0,
                              logical_filename,
-                             xmlGetLineNo (n2),
+                             xmlGetLineNo (n),
                              _("\
 The element <%s> does not have attribute <%s>"),
                              "pluralRule", "count");
               break;
             }
 
-          count = xmlGetProp (n2, BAD_CAST "count");
-          content = xmlNodeGetContent (n2);
+          count = xmlGetProp (n, BAD_CAST "count");
+          content = xmlNodeGetContent (n);
           length = xmlStrlen (count) + strlen (": ")
             + xmlStrlen (content) + strlen ("; ");
 
-- 
2.9.0.windows.1

