summaryrefslogtreecommitdiffstats
path: root/gettext-tools/src/locating-rule.c
diff options
context:
space:
mode:
Diffstat (limited to 'gettext-tools/src/locating-rule.c')
-rw-r--r--gettext-tools/src/locating-rule.c437
1 files changed, 437 insertions, 0 deletions
diff --git a/gettext-tools/src/locating-rule.c b/gettext-tools/src/locating-rule.c
new file mode 100644
index 0000000..2a1de19
--- /dev/null
+++ b/gettext-tools/src/locating-rule.c
@@ -0,0 +1,437 @@
+/* XML resource locating rules
+ 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
+
+/* Specification. */
+#include "locating-rule.h"
+
+#include "basename.h"
+#include "concat-filename.h"
+#include "c-strcase.h"
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+
+#if HAVE_DIRENT_H
+# define HAVE_DIR 1
+#else
+# define HAVE_DIR 0
+#endif
+
+#include "dir-list.h"
+#include <errno.h>
+#include "error.h"
+#include "filename.h"
+#include <fnmatch.h>
+#include "gettext.h"
+#include "hash.h"
+#include <libxml/parser.h>
+#include <libxml/uri.h>
+#include "xalloc.h"
+
+#define _(str) gettext (str)
+
+#define LOCATING_RULES_NS "https://www.gnu.org/s/gettext/ns/locating-rules/1.0"
+
+struct document_locating_rule_ty
+{
+ char *ns;
+ char *local_name;
+
+ char *target;
+};
+
+struct document_locating_rule_list_ty
+{
+ struct document_locating_rule_ty *items;
+ size_t nitems;
+ size_t nitems_max;
+};
+
+struct locating_rule_ty
+{
+ char *pattern;
+ char *name;
+
+ struct document_locating_rule_list_ty doc_rules;
+ char *target;
+};
+
+struct locating_rule_list_ty
+{
+ struct locating_rule_ty *items;
+ size_t nitems;
+ size_t nitems_max;
+};
+
+static char *
+get_attribute (xmlNode *node, const char *attr)
+{
+ xmlChar *value;
+ char *result;
+
+ value = xmlGetProp (node, BAD_CAST attr);
+ result = xstrdup ((const char *) value);
+ xmlFree (value);
+
+ return result;
+}
+
+static const char *
+document_locating_rule_match (struct document_locating_rule_ty *rule,
+ xmlDoc *doc)
+{
+ xmlNode *root;
+
+ root = xmlDocGetRootElement (doc);
+ if (rule->ns != NULL)
+ {
+ if (root->ns == NULL
+ || !xmlStrEqual (root->ns->href, BAD_CAST rule->ns))
+ return NULL;
+ }
+
+ if (rule->local_name != NULL)
+ {
+ if (!xmlStrEqual (root->name,
+ BAD_CAST rule->local_name))
+ return NULL;
+ }
+
+ return rule->target;
+}
+
+static const char *
+locating_rule_match (struct locating_rule_ty *rule,
+ const char *filename,
+ const char *name)
+{
+ if (name != NULL)
+ {
+ if (rule->name == NULL || c_strcasecmp (name, rule->name) != 0)
+ return NULL;
+ }
+ else
+ {
+ const char *base;
+ char *reduced;
+ int err;
+
+ base = strrchr (filename, '/');
+ if (!base)
+ base = filename;
+
+ reduced = xstrdup (base);
+ /* Remove a trailing ".in" - it's a generic suffix. */
+ while (strlen (reduced) >= 3
+ && memcmp (reduced + strlen (reduced) - 3, ".in", 3) == 0)
+ reduced[strlen (reduced) - 3] = '\0';
+
+ err = fnmatch (rule->pattern, basename (reduced), FNM_PATHNAME);
+ free (reduced);
+ if (err != 0)
+ return NULL;
+ }
+
+ /* Check documentRules. */
+ if (rule->doc_rules.nitems > 0)
+ {
+ const char *target;
+ xmlDoc *doc;
+ size_t i;
+
+ doc = xmlReadFile (filename, NULL,
+ XML_PARSE_NONET
+ | XML_PARSE_NOWARNING
+ | XML_PARSE_NOBLANKS
+ | XML_PARSE_NOERROR);
+ if (doc == NULL)
+ {
+ xmlError *err = xmlGetLastError ();
+ error (0, 0, _("cannot read %s: %s"), filename, err->message);
+ return NULL;
+ }
+
+ for (i = 0, target = NULL; i < rule->doc_rules.nitems; i++)
+ {
+ target =
+ document_locating_rule_match (&rule->doc_rules.items[i], doc);
+ if (target)
+ break;
+ }
+ xmlFreeDoc (doc);
+ if (target != NULL)
+ return target;
+ }
+
+ if (rule->target != NULL)
+ return rule->target;
+
+ return NULL;
+}
+
+const char *
+locating_rule_list_locate (struct locating_rule_list_ty *rules,
+ const char *filename,
+ const char *name)
+{
+ const char *target = NULL;
+ size_t i;
+
+ for (i = 0; i < rules->nitems; i++)
+ {
+ if (IS_ABSOLUTE_PATH (filename))
+ {
+ target = locating_rule_match (&rules->items[i], filename, name);
+ if (target != NULL)
+ return target;
+ }
+ else
+ {
+ int j;
+
+ for (j = 0; ; ++j)
+ {
+ const char *dir = dir_list_nth (j);
+ char *new_filename;
+
+ if (dir == NULL)
+ break;
+
+ new_filename = xconcatenated_filename (dir, filename, NULL);
+ target = locating_rule_match (&rules->items[i], new_filename,
+ name);
+ free (new_filename);
+ if (target != NULL)
+ return target;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void
+missing_attribute (xmlNode *node, const char *attribute)
+{
+ error (0, 0, _("\"%s\" node does not have \"%s\""), node->name, attribute);
+}
+
+static void
+document_locating_rule_destroy (struct document_locating_rule_ty *rule)
+{
+ free (rule->ns);
+ free (rule->local_name);
+ free (rule->target);
+}
+
+static void
+document_locating_rule_list_add (struct document_locating_rule_list_ty *rules,
+ xmlNode *node)
+{
+ struct document_locating_rule_ty rule;
+
+ if (!xmlHasProp (node, BAD_CAST "target"))
+ {
+ missing_attribute (node, "target");
+ return;
+ }
+
+ memset (&rule, 0, sizeof (struct document_locating_rule_ty));
+
+ if (xmlHasProp (node, BAD_CAST "ns"))
+ rule.ns = get_attribute (node, "ns");
+ if (xmlHasProp (node, BAD_CAST "localName"))
+ rule.local_name = get_attribute (node, "localName");
+ rule.target = get_attribute (node, "target");
+
+ if (rules->nitems == rules->nitems_max)
+ {
+ rules->nitems_max = 2 * rules->nitems_max + 1;
+ rules->items =
+ xrealloc (rules->items,
+ sizeof (struct document_locating_rule_ty)
+ * rules->nitems_max);
+ }
+ memcpy (&rules->items[rules->nitems++], &rule,
+ sizeof (struct document_locating_rule_ty));
+}
+
+static void
+locating_rule_destroy (struct locating_rule_ty *rule)
+{
+ size_t i;
+
+ for (i = 0; i < rule->doc_rules.nitems; i++)
+ document_locating_rule_destroy (&rule->doc_rules.items[i]);
+ free (rule->doc_rules.items);
+
+ free (rule->name);
+ free (rule->pattern);
+ free (rule->target);
+}
+
+static bool
+locating_rule_list_add_from_file (struct locating_rule_list_ty *rules,
+ const char *rule_file_name)
+{
+ xmlDoc *doc;
+ xmlNode *root, *node;
+
+ doc = xmlReadFile (rule_file_name, "utf-8",
+ XML_PARSE_NONET
+ | XML_PARSE_NOWARNING
+ | XML_PARSE_NOBLANKS
+ | XML_PARSE_NOERROR);
+ if (doc == NULL)
+ {
+ error (0, 0, _("cannot read XML file %s"), rule_file_name);
+ return false;
+ }
+
+ root = xmlDocGetRootElement (doc);
+ if (!(xmlStrEqual (root->name, BAD_CAST "locatingRules")
+#if 0
+ && root->ns
+ && xmlStrEqual (root->ns->href, BAD_CAST LOCATING_RULES_NS)
+#endif
+ ))
+ {
+ error (0, 0, _("the root element is not \"locatingRules\""));
+ xmlFreeDoc (doc);
+ return false;
+ }
+
+ for (node = root->children; node; node = node->next)
+ {
+ if (xmlStrEqual (node->name, BAD_CAST "locatingRule"))
+ {
+ struct locating_rule_ty rule;
+
+ if (!xmlHasProp (node, BAD_CAST "pattern"))
+ {
+ missing_attribute (node, "pattern");
+ xmlFreeDoc (doc);
+ continue;
+ }
+
+ memset (&rule, 0, sizeof (struct locating_rule_ty));
+ rule.pattern = get_attribute (node, "pattern");
+ if (xmlHasProp (node, BAD_CAST "name"))
+ rule.name = get_attribute (node, "name");
+ if (xmlHasProp (node, BAD_CAST "target"))
+ rule.target = get_attribute (node, "target");
+ else
+ {
+ xmlNode *n;
+
+ for (n = node->children; n; n = n->next)
+ {
+ if (xmlStrEqual (n->name, BAD_CAST "documentRule"))
+ document_locating_rule_list_add (&rule.doc_rules, n);
+ }
+ }
+ if (rules->nitems == rules->nitems_max)
+ {
+ rules->nitems_max = 2 * rules->nitems_max + 1;
+ rules->items =
+ xrealloc (rules->items,
+ sizeof (struct locating_rule_ty) * rules->nitems_max);
+ }
+ memcpy (&rules->items[rules->nitems++], &rule,
+ sizeof (struct locating_rule_ty));
+ }
+ }
+
+ xmlFreeDoc (doc);
+ return true;
+}
+
+bool
+locating_rule_list_add_from_directory (struct locating_rule_list_ty *rules,
+ const char *directory)
+{
+#if HAVE_DIR
+ DIR *dirp;
+
+ dirp = opendir (directory);
+ if (dirp == NULL)
+ return false;
+
+ for (;;)
+ {
+ struct dirent *dp;
+
+ errno = 0;
+ dp = readdir (dirp);
+ if (dp != NULL)
+ {
+ const char *name = dp->d_name;
+ size_t namlen = strlen (name);
+
+ if (namlen > 4 && memcmp (name + namlen - 4, ".loc", 4) == 0)
+ {
+ char *locator_file_name =
+ xconcatenated_filename (directory, name, NULL);
+ locating_rule_list_add_from_file (rules, locator_file_name);
+ free (locator_file_name);
+ }
+ }
+ else if (errno != 0)
+ return false;
+ else
+ break;
+ }
+ if (closedir (dirp))
+ return false;
+
+#endif
+ return true;
+}
+
+struct locating_rule_list_ty *
+locating_rule_list_alloc (void)
+{
+ struct locating_rule_list_ty *result;
+
+ xmlCheckVersion (LIBXML_VERSION);
+
+ result = XCALLOC (1, struct locating_rule_list_ty);
+
+ return result;
+}
+
+void
+locating_rule_list_destroy (struct locating_rule_list_ty *rules)
+{
+ while (rules->nitems-- > 0)
+ locating_rule_destroy (&rules->items[rules->nitems]);
+ free (rules->items);
+}
+
+void
+locating_rule_list_free (struct locating_rule_list_ty *rules)
+{
+ if (rules != NULL)
+ locating_rule_list_destroy (rules);
+ free (rules);
+}