summaryrefslogtreecommitdiff
path: root/gettext-tools/src/cldr-plurals.c
diff options
context:
space:
mode:
Diffstat (limited to 'gettext-tools/src/cldr-plurals.c')
-rw-r--r--gettext-tools/src/cldr-plurals.c370
1 files changed, 370 insertions, 0 deletions
diff --git a/gettext-tools/src/cldr-plurals.c b/gettext-tools/src/cldr-plurals.c
new file mode 100644
index 0000000..abbd0c2
--- /dev/null
+++ b/gettext-tools/src/cldr-plurals.c
@@ -0,0 +1,370 @@
+/* Unicode CLDR plural rule parser and converter
+ Copyright (C) 2015 Free Software Foundation, Inc.
+
+ This file was written by Daiki Ueno <ueno@gnu.org>, 2015.
+
+ This program 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.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "basename.h"
+#include "cldr-plural-exp.h"
+#include "c-ctype.h"
+#include <errno.h>
+#include <error.h>
+#include <getopt.h>
+#include "gettext.h"
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <locale.h>
+#include "progname.h"
+#include "propername.h"
+#include "relocatable.h"
+#include <stdlib.h>
+#include <string.h>
+#include "xalloc.h"
+
+#define _(s) gettext(s)
+
+
+static char *
+extract_rules (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ const char *locale)
+{
+ xmlDocPtr doc;
+ xmlNodePtr node, n;
+ size_t locale_length;
+ char *buffer = NULL, *p;
+ size_t bufmax = 0;
+ size_t buflen = 0;
+
+ doc = xmlReadFd (fileno (fp), logical_filename, NULL,
+ XML_PARSE_NONET
+ | XML_PARSE_NOWARNING
+ | XML_PARSE_NOERROR
+ | XML_PARSE_NOBLANKS);
+ if (doc == NULL)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+
+ node = xmlDocGetRootElement (doc);
+ if (!node || !xmlStrEqual (node->name, BAD_CAST "supplementalData"))
+ {
+ error_at_line (0, 0,
+ logical_filename,
+ xmlGetLineNo (node),
+ _("\
+The root element must be <%s>"),
+ "supplementalData");
+ goto out;
+ }
+
+ for (n = node->children; n; n = n->next)
+ {
+ if (n->type == XML_ELEMENT_NODE
+ && xmlStrEqual (n->name, BAD_CAST "plurals"))
+ break;
+ }
+ if (!n)
+ {
+ 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)
+ {
+ 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"))
+ {
+ error_at_line (0, 0,
+ logical_filename,
+ xmlGetLineNo (n),
+ _("\
+The element <%s> does not have attribute <%s>"),
+ "pluralRules", "locales");
+ 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))))
+ {
+ found = true;
+ break;
+ }
+ while (*cp && !c_isspace (*cp))
+ cp++;
+ }
+ xmlFree (locales);
+
+ if (!found)
+ continue;
+
+ for (n2 = n->children; n2; n2 = n2->next)
+ {
+ xmlChar *count;
+ xmlChar *content;
+ size_t length;
+
+ if (n2->type != XML_ELEMENT_NODE
+ || !xmlStrEqual (n2->name, BAD_CAST "pluralRule"))
+ continue;
+
+ if (!xmlHasProp (n2, BAD_CAST "count"))
+ {
+ error_at_line (0, 0,
+ logical_filename,
+ xmlGetLineNo (n2),
+ _("\
+The element <%s> does not have attribute <%s>"),
+ "pluralRule", "count");
+ break;
+ }
+
+ count = xmlGetProp (n2, BAD_CAST "count");
+ content = xmlNodeGetContent (n2);
+ length = xmlStrlen (count) + strlen (": ")
+ + xmlStrlen (content) + strlen ("; ");
+
+ if (buflen + length + 1 > bufmax)
+ {
+ bufmax *= 2;
+ if (bufmax < buflen + length + 1)
+ bufmax = buflen + length + 1;
+ buffer = (char *) xrealloc (buffer, bufmax);
+ }
+
+ sprintf (buffer + buflen, "%s: %s; ", count, content);
+ xmlFree (count);
+ xmlFree (content);
+
+ buflen += length;
+ }
+ }
+
+ if (buffer)
+ {
+ /* Scrub the last semicolon, if any. */
+ p = strrchr (buffer, ';');
+ if (p)
+ *p = '\0';
+ }
+
+ out:
+ xmlFreeDoc (doc);
+ return buffer;
+}
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION...] [LOCALE RULES]...\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Extract or convert Unicode CLDR plural rules.\n\
+\n\
+If both LOCALE and RULES are specified, it reads CLDR plural rules for\n\
+LOCALE from RULES and print them in a form suitable for gettext use.\n\
+If no argument is given, it reads CLDR plural rules from the standard input.\n\
+"));
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+Similarly for optional arguments.\n\
+"));
+ printf ("\n");
+ printf (_("\
+ -c, --cldr print plural rules in the CLDR format\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+ exit (status);
+}
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "cldr", no_argument, NULL, 'c' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+int
+main (int argc, char **argv)
+{
+ bool opt_cldr_format = false;
+ bool do_help = false;
+ bool do_version = false;
+ int optchar;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ while ((optchar = getopt_long (argc, argv, "chV", long_options, NULL)) != EOF)
+ switch (optchar)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'c':
+ opt_cldr_format = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ /* Version information requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2015");
+ printf (_("Written by %s.\n"), proper_name ("Daiki Ueno"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ if (argc == optind + 2)
+ {
+ /* Two arguments: Read CLDR rules from a file. */
+ const char *locale = argv[optind];
+ const char *logical_filename = argv[optind + 1];
+ char *extracted_rules;
+ FILE *fp;
+
+ LIBXML_TEST_VERSION
+
+ fp = fopen (logical_filename, "r");
+ if (fp == NULL)
+ error (1, 0, _("%s cannot be read"), logical_filename);
+
+ extracted_rules = extract_rules (fp, logical_filename, logical_filename,
+ locale);
+ fclose (fp);
+ if (extracted_rules == NULL)
+ error (1, 0, _("cannot extract rules for %s"), locale);
+
+ if (opt_cldr_format)
+ printf ("%s\n", extracted_rules);
+ else
+ {
+ struct cldr_plural_rule_list_ty *result;
+
+ result = cldr_plural_parse (extracted_rules);
+ if (result == NULL)
+ error (1, 0, _("cannot parse CLDR rule"));
+
+ cldr_plural_rule_list_print (result, stdout);
+ cldr_plural_rule_list_free (result);
+ }
+ free (extracted_rules);
+ }
+ else if (argc == optind)
+ {
+ /* No argument: Read CLDR rules from standard input. */
+ char *line = NULL;
+ size_t line_size = 0;
+ for (;;)
+ {
+ int line_len;
+ struct cldr_plural_rule_list_ty *result;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ result = cldr_plural_parse (line);
+ if (result)
+ {
+ cldr_plural_rule_list_print (result, stdout);
+ cldr_plural_rule_list_free (result);
+ }
+ }
+
+ free (line);
+ }
+ else
+ {
+ error (1, 0, _("extra operand %s"), argv[optind]);
+ }
+
+ return 0;
+}