• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* XML resource locating rules
2    Copyright (C) 2015, 2019-2020 Free Software Foundation, Inc.
3 
4    This file was written by Daiki Ueno <ueno@gnu.org>, 2015.
5 
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
18 
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 
23 /* Specification.  */
24 #include "locating-rule.h"
25 
26 #include "basename-lgpl.h"
27 #include "concat-filename.h"
28 #include "c-strcase.h"
29 
30 #if HAVE_DIRENT_H
31 # include <dirent.h>
32 #endif
33 
34 #if HAVE_DIRENT_H
35 # define HAVE_DIR 1
36 #else
37 # define HAVE_DIR 0
38 #endif
39 
40 #include "dir-list.h"
41 #include <errno.h>
42 #include "error.h"
43 #include "filename.h"
44 #include <fnmatch.h>
45 #include "gettext.h"
46 #include "mem-hash-map.h"
47 #include <libxml/parser.h>
48 #include <libxml/uri.h>
49 #include "xalloc.h"
50 
51 #define _(str) gettext (str)
52 
53 #define LOCATING_RULES_NS "https://www.gnu.org/s/gettext/ns/locating-rules/1.0"
54 
55 struct document_locating_rule_ty
56 {
57   char *ns;
58   char *local_name;
59 
60   char *target;
61 };
62 
63 struct document_locating_rule_list_ty
64 {
65   struct document_locating_rule_ty *items;
66   size_t nitems;
67   size_t nitems_max;
68 };
69 
70 struct locating_rule_ty
71 {
72   char *pattern;
73   char *name;
74 
75   struct document_locating_rule_list_ty doc_rules;
76   char *target;
77 };
78 
79 struct locating_rule_list_ty
80 {
81   struct locating_rule_ty *items;
82   size_t nitems;
83   size_t nitems_max;
84 };
85 
86 static char *
get_attribute(xmlNode * node,const char * attr)87 get_attribute (xmlNode *node, const char *attr)
88 {
89   xmlChar *value;
90   char *result;
91 
92   value = xmlGetProp (node, BAD_CAST attr);
93   result = xstrdup ((const char *) value);
94   xmlFree (value);
95 
96   return result;
97 }
98 
99 static const char *
document_locating_rule_match(struct document_locating_rule_ty * rule,xmlDoc * doc)100 document_locating_rule_match (struct document_locating_rule_ty *rule,
101                               xmlDoc *doc)
102 {
103   xmlNode *root;
104 
105   root = xmlDocGetRootElement (doc);
106   if (rule->ns != NULL)
107     {
108       if (root->ns == NULL
109           || !xmlStrEqual (root->ns->href, BAD_CAST rule->ns))
110         return NULL;
111     }
112 
113   if (rule->local_name != NULL)
114     {
115       if (!xmlStrEqual (root->name,
116                         BAD_CAST rule->local_name))
117         return NULL;
118     }
119 
120   return rule->target;
121 }
122 
123 static const char *
locating_rule_match(struct locating_rule_ty * rule,const char * filename,const char * name)124 locating_rule_match (struct locating_rule_ty *rule,
125                      const char *filename,
126                      const char *name)
127 {
128   if (name != NULL)
129     {
130       if (rule->name == NULL || c_strcasecmp (name, rule->name) != 0)
131         return NULL;
132     }
133   else
134     {
135       const char *base;
136       char *reduced;
137       int err;
138 
139       base = strrchr (filename, '/');
140       if (!base)
141         base = filename;
142 
143       reduced = xstrdup (base);
144       /* Remove a trailing ".in" - it's a generic suffix.  */
145       while (strlen (reduced) >= 3
146              && memcmp (reduced + strlen (reduced) - 3, ".in", 3) == 0)
147         reduced[strlen (reduced) - 3] = '\0';
148 
149       err = fnmatch (rule->pattern, last_component (reduced), FNM_PATHNAME);
150       free (reduced);
151       if (err != 0)
152         return NULL;
153     }
154 
155   /* Check documentRules.  */
156   if (rule->doc_rules.nitems > 0)
157     {
158       const char *target;
159       xmlDoc *doc;
160       size_t i;
161 
162       doc = xmlReadFile (filename, NULL,
163                          XML_PARSE_NONET
164                          | XML_PARSE_NOWARNING
165                          | XML_PARSE_NOBLANKS
166                          | XML_PARSE_NOERROR);
167       if (doc == NULL)
168         {
169           xmlError *err = xmlGetLastError ();
170           error (0, 0, _("cannot read %s: %s"), filename, err->message);
171           return NULL;
172         }
173 
174       for (i = 0, target = NULL; i < rule->doc_rules.nitems; i++)
175         {
176           target =
177             document_locating_rule_match (&rule->doc_rules.items[i], doc);
178           if (target)
179             break;
180         }
181       xmlFreeDoc (doc);
182       if (target != NULL)
183         return target;
184     }
185 
186   if (rule->target != NULL)
187     return rule->target;
188 
189   return NULL;
190 }
191 
192 const char *
locating_rule_list_locate(struct locating_rule_list_ty * rules,const char * filename,const char * name)193 locating_rule_list_locate (struct locating_rule_list_ty *rules,
194                            const char *filename,
195                            const char *name)
196 {
197   size_t i;
198 
199   for (i = 0; i < rules->nitems; i++)
200     {
201       if (IS_RELATIVE_FILE_NAME (filename))
202         {
203           int j;
204 
205           for (j = 0; ; ++j)
206             {
207               const char *dir = dir_list_nth (j);
208               char *new_filename;
209               const char *target;
210 
211               if (dir == NULL)
212                 break;
213 
214               new_filename = xconcatenated_filename (dir, filename, NULL);
215               target = locating_rule_match (&rules->items[i], new_filename,
216                                             name);
217               free (new_filename);
218               if (target != NULL)
219                 return target;
220             }
221         }
222       else
223         {
224           const char *target =
225             locating_rule_match (&rules->items[i], filename, name);
226 
227           if (target != NULL)
228             return target;
229         }
230     }
231 
232   return NULL;
233 }
234 
235 static void
missing_attribute(xmlNode * node,const char * attribute)236 missing_attribute (xmlNode *node, const char *attribute)
237 {
238   error (0, 0, _("\"%s\" node does not have \"%s\""), node->name, attribute);
239 }
240 
241 static void
document_locating_rule_destroy(struct document_locating_rule_ty * rule)242 document_locating_rule_destroy (struct document_locating_rule_ty *rule)
243 {
244   free (rule->ns);
245   free (rule->local_name);
246   free (rule->target);
247 }
248 
249 static void
document_locating_rule_list_add(struct document_locating_rule_list_ty * rules,xmlNode * node)250 document_locating_rule_list_add (struct document_locating_rule_list_ty *rules,
251                                  xmlNode *node)
252 {
253   struct document_locating_rule_ty rule;
254 
255   if (!xmlHasProp (node, BAD_CAST "target"))
256     {
257       missing_attribute (node, "target");
258       return;
259     }
260 
261   memset (&rule, 0, sizeof (struct document_locating_rule_ty));
262 
263   if (xmlHasProp (node, BAD_CAST "ns"))
264     rule.ns = get_attribute (node, "ns");
265   if (xmlHasProp (node, BAD_CAST "localName"))
266     rule.local_name = get_attribute (node, "localName");
267   rule.target = get_attribute (node, "target");
268 
269   if (rules->nitems == rules->nitems_max)
270     {
271       rules->nitems_max = 2 * rules->nitems_max + 1;
272       rules->items =
273         xrealloc (rules->items,
274                   sizeof (struct document_locating_rule_ty)
275                   * rules->nitems_max);
276     }
277   memcpy (&rules->items[rules->nitems++], &rule,
278           sizeof (struct document_locating_rule_ty));
279 }
280 
281 static void
locating_rule_destroy(struct locating_rule_ty * rule)282 locating_rule_destroy (struct locating_rule_ty *rule)
283 {
284   size_t i;
285 
286   for (i = 0; i < rule->doc_rules.nitems; i++)
287     document_locating_rule_destroy (&rule->doc_rules.items[i]);
288   free (rule->doc_rules.items);
289 
290   free (rule->name);
291   free (rule->pattern);
292   free (rule->target);
293 }
294 
295 static bool
locating_rule_list_add_from_file(struct locating_rule_list_ty * rules,const char * rule_file_name)296 locating_rule_list_add_from_file (struct locating_rule_list_ty *rules,
297                                   const char *rule_file_name)
298 {
299   xmlDoc *doc;
300   xmlNode *root, *node;
301 
302   doc = xmlReadFile (rule_file_name, "utf-8",
303                      XML_PARSE_NONET
304                      | XML_PARSE_NOWARNING
305                      | XML_PARSE_NOBLANKS
306                      | XML_PARSE_NOERROR);
307   if (doc == NULL)
308     {
309       error (0, 0, _("cannot read XML file %s"), rule_file_name);
310       return false;
311     }
312 
313   root = xmlDocGetRootElement (doc);
314   if (!(xmlStrEqual (root->name, BAD_CAST "locatingRules")
315 #if 0
316         && root->ns
317         && xmlStrEqual (root->ns->href, BAD_CAST LOCATING_RULES_NS)
318 #endif
319         ))
320     {
321       error (0, 0, _("the root element is not \"locatingRules\""));
322       xmlFreeDoc (doc);
323       return false;
324     }
325 
326   for (node = root->children; node; node = node->next)
327     {
328       if (xmlStrEqual (node->name, BAD_CAST "locatingRule"))
329         {
330           struct locating_rule_ty rule;
331 
332           if (!xmlHasProp (node, BAD_CAST "pattern"))
333             {
334               missing_attribute (node, "pattern");
335               xmlFreeDoc (doc);
336             }
337           else
338             {
339               memset (&rule, 0, sizeof (struct locating_rule_ty));
340               rule.pattern = get_attribute (node, "pattern");
341               if (xmlHasProp (node, BAD_CAST "name"))
342                 rule.name = get_attribute (node, "name");
343               if (xmlHasProp (node, BAD_CAST "target"))
344                 rule.target = get_attribute (node, "target");
345               else
346                 {
347                   xmlNode *n;
348 
349                   for (n = node->children; n; n = n->next)
350                     {
351                       if (xmlStrEqual (n->name, BAD_CAST "documentRule"))
352                         document_locating_rule_list_add (&rule.doc_rules, n);
353                     }
354                 }
355               if (rules->nitems == rules->nitems_max)
356                 {
357                   rules->nitems_max = 2 * rules->nitems_max + 1;
358                   rules->items =
359                     xrealloc (rules->items,
360                               sizeof (struct locating_rule_ty) * rules->nitems_max);
361                 }
362               memcpy (&rules->items[rules->nitems++], &rule,
363                       sizeof (struct locating_rule_ty));
364             }
365         }
366     }
367 
368   xmlFreeDoc (doc);
369   return true;
370 }
371 
372 bool
locating_rule_list_add_from_directory(struct locating_rule_list_ty * rules,const char * directory)373 locating_rule_list_add_from_directory (struct locating_rule_list_ty *rules,
374                                        const char *directory)
375 {
376 #if HAVE_DIR
377   DIR *dirp;
378 
379   dirp = opendir (directory);
380   if (dirp == NULL)
381     return false;
382 
383   for (;;)
384     {
385       struct dirent *dp;
386 
387       errno = 0;
388       dp = readdir (dirp);
389       if (dp != NULL)
390         {
391           const char *name = dp->d_name;
392           size_t namlen = strlen (name);
393 
394           if (namlen > 4 && memcmp (name + namlen - 4, ".loc", 4) == 0)
395             {
396               char *locator_file_name =
397                 xconcatenated_filename (directory, name, NULL);
398               locating_rule_list_add_from_file (rules, locator_file_name);
399               free (locator_file_name);
400             }
401         }
402       else if (errno != 0)
403         return false;
404       else
405         break;
406     }
407   if (closedir (dirp))
408     return false;
409 
410 #endif
411   return true;
412 }
413 
414 struct locating_rule_list_ty *
locating_rule_list_alloc(void)415 locating_rule_list_alloc (void)
416 {
417   struct locating_rule_list_ty *result;
418 
419   xmlCheckVersion (LIBXML_VERSION);
420 
421   result = XCALLOC (1, struct locating_rule_list_ty);
422 
423   return result;
424 }
425 
426 void
locating_rule_list_destroy(struct locating_rule_list_ty * rules)427 locating_rule_list_destroy (struct locating_rule_list_ty *rules)
428 {
429   while (rules->nitems-- > 0)
430     locating_rule_destroy (&rules->items[rules->nitems]);
431   free (rules->items);
432 }
433 
434 void
locating_rule_list_free(struct locating_rule_list_ty * rules)435 locating_rule_list_free (struct locating_rule_list_ty *rules)
436 {
437   if (rules != NULL)
438     locating_rule_list_destroy (rules);
439   free (rules);
440 }
441