Line data Source code
1 : /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 : /*
3 : * lt-xml.c
4 : * Copyright (C) 2011-2012 Akira TAGOH
5 : *
6 : * Authors:
7 : * Akira TAGOH <akira@tagoh.org>
8 : *
9 : * You may distribute under the terms of either the GNU
10 : * Lesser General Public License or the Mozilla Public
11 : * License, as specified in the README file.
12 : */
13 : #ifdef HAVE_CONFIG_H
14 : #include "config.h"
15 : #endif
16 :
17 : #ifndef _WIN32
18 : #include <pthread.h>
19 : #else
20 : #include <windows.h>
21 : #endif
22 : #include <sys/stat.h>
23 : #include <libxml/parser.h>
24 : #include <libxml/xpath.h>
25 : #include "lt-error.h"
26 : #include "lt-mem.h"
27 : #include "lt-messages.h"
28 : #include "lt-database.h"
29 : #include "lt-string.h"
30 : #include "lt-xml.h"
31 :
32 :
33 : struct _lt_xml_t {
34 : lt_mem_t parent;
35 : xmlDocPtr subtag_registry;
36 : xmlDocPtr cldr_bcp47_calendar;
37 : xmlDocPtr cldr_bcp47_collation;
38 : xmlDocPtr cldr_bcp47_currency;
39 : xmlDocPtr cldr_bcp47_number;
40 : xmlDocPtr cldr_bcp47_timezone;
41 : xmlDocPtr cldr_bcp47_transform;
42 : xmlDocPtr cldr_bcp47_variant;
43 : xmlDocPtr cldr_supplemental_likelysubtags;
44 : };
45 :
46 : static lt_xml_t *__xml = NULL;
47 : #ifndef _WIN32
48 : static pthread_mutex_t __lt_xml_lock = PTHREAD_MUTEX_INITIALIZER;
49 : #endif
50 :
51 : /*< private >*/
52 : static lt_bool_t
53 4 : lt_xml_read_subtag_registry(lt_xml_t *xml,
54 : lt_error_t **error)
55 : {
56 : lt_string_t *regfile;
57 4 : xmlParserCtxtPtr xmlparser = NULL;
58 4 : xmlDocPtr doc = NULL;
59 4 : lt_error_t *err = NULL;
60 :
61 4 : lt_return_val_if_fail (xml != NULL, FALSE);
62 :
63 4 : regfile = lt_string_new(NULL);
64 : #ifdef GNOME_ENABLE_DEBUG
65 : LT_STMT_START {
66 : struct stat st;
67 :
68 : lt_string_append_filename(regfile,
69 : BUILDDIR,
70 : "data", "language-subtag-reegistry.xml", NULL);
71 : if (stat(lt_string_value(regfile), &st) == -1) {
72 : lt_string_clear(regfile);
73 : #endif
74 4 : lt_string_append_filename(regfile,
75 : lt_db_get_datadir(),
76 : "language-subtag-registry.xml", NULL);
77 : #ifdef GNOME_ENABLE_DEBUG
78 : }
79 : } LT_STMT_END;
80 : #endif
81 4 : xmlparser = xmlNewParserCtxt();
82 4 : if (!xmlparser) {
83 0 : lt_error_set(&err, LT_ERR_OOM,
84 : "Unable to create an instance of xmlParserCtxt.");
85 0 : goto bail;
86 : }
87 4 : doc = xmlCtxtReadFile(xmlparser, lt_string_value(regfile), "UTF-8", 0);
88 4 : if (!doc) {
89 0 : lt_error_set(&err, LT_ERR_FAIL_ON_XML,
90 : "Unable to read the xml file: %s",
91 : lt_string_value(regfile));
92 0 : goto bail;
93 : }
94 4 : xml->subtag_registry = doc;
95 4 : lt_mem_add_ref(&xml->parent, xml->subtag_registry,
96 : (lt_destroy_func_t)xmlFreeDoc);
97 :
98 : bail:
99 4 : lt_string_unref(regfile);
100 4 : if (xmlparser)
101 4 : xmlFreeParserCtxt(xmlparser);
102 :
103 4 : if (lt_error_is_set(err, LT_ERR_ANY)) {
104 0 : if (error)
105 0 : *error = lt_error_ref(err);
106 : else
107 0 : lt_error_print(err, LT_ERR_ANY);
108 0 : lt_error_unref(err);
109 :
110 0 : return FALSE;
111 : }
112 :
113 4 : return TRUE;
114 : }
115 :
116 : static lt_bool_t
117 44 : lt_xml_read_cldr_bcp47(lt_xml_t *xml,
118 : const char *filename,
119 : xmlDocPtr *doc,
120 : lt_error_t **error)
121 : {
122 : lt_string_t *regfile;
123 44 : xmlParserCtxtPtr xmlparser = NULL;
124 44 : lt_error_t *err = NULL;
125 :
126 44 : lt_return_val_if_fail (xml != NULL, FALSE);
127 :
128 44 : regfile = lt_string_new(NULL);
129 : #ifdef GNOME_ENABLE_DEBUG
130 : LT_STMT_START {
131 : struct stat st;
132 :
133 : lt_string_append_filename(regfile,
134 : SRCDIR, "data", "common", "bcp47",
135 : filename, NULL);
136 : if (stat(lt_string_value(regfile), &st) == -1) {
137 : lt_string_clear(regfile);
138 : #endif
139 44 : lt_string_append_filename(regfile,
140 : lt_db_get_datadir(),
141 : "common", "bcp47", filename, NULL);
142 : #ifdef GNOME_ENABLE_DEBUG
143 : }
144 : } LT_STMT_END;
145 : #endif
146 44 : xmlparser = xmlNewParserCtxt();
147 44 : if (!xmlparser) {
148 0 : lt_error_set(&err, LT_ERR_OOM,
149 : "Unable to create an instance of xmlParserCtxt.");
150 0 : goto bail;
151 : }
152 44 : *doc = xmlCtxtReadFile(xmlparser, lt_string_value(regfile), "UTF-8", 0);
153 44 : if (!*doc) {
154 0 : lt_error_set(&err, LT_ERR_FAIL_ON_XML,
155 : "Unable to read the xml file: %s",
156 : lt_string_value(regfile));
157 0 : goto bail;
158 : }
159 44 : lt_mem_add_ref(&xml->parent, *doc,
160 : (lt_destroy_func_t)xmlFreeDoc);
161 :
162 : bail:
163 44 : lt_string_unref(regfile);
164 44 : if (xmlparser)
165 44 : xmlFreeParserCtxt(xmlparser);
166 :
167 44 : if (lt_error_is_set(err, LT_ERR_ANY)) {
168 0 : if (error)
169 0 : *error = lt_error_ref(err);
170 : else
171 0 : lt_error_print(err, LT_ERR_ANY);
172 0 : lt_error_unref(err);
173 :
174 0 : return FALSE;
175 : }
176 :
177 44 : return TRUE;
178 : }
179 :
180 : static lt_bool_t
181 4 : lt_xml_read_cldr_supplemental(lt_xml_t *xml,
182 : const char *filename,
183 : xmlDocPtr *doc,
184 : lt_error_t **error)
185 : {
186 4 : lt_string_t *regfile = NULL;
187 4 : xmlParserCtxtPtr xmlparser = NULL;
188 4 : lt_error_t *err = NULL;
189 :
190 4 : lt_return_val_if_fail (xml != NULL, FALSE);
191 :
192 4 : regfile = lt_string_new(NULL);
193 : #ifdef GNOME_ENABLE_DEBUG
194 : LT_STMT_START {
195 : struct stat st;
196 :
197 : lt_string_append_filename(regfile,
198 : SRCDIR, "data", "common", "supplemental",
199 : filename, NULL);
200 : if (stat(lt_string_value(regfile), &st) == -1) {
201 : lt_string_clear(regfile);
202 : #endif
203 4 : lt_string_append_filename(regfile,
204 : lt_db_get_datadir(),
205 : "common", "supplemental", filename, NULL);
206 : #ifdef GNOME_ENABLE_DEBUG
207 : }
208 : } LT_STMT_END;
209 : #endif
210 4 : xmlparser = xmlNewParserCtxt();
211 4 : if (!xmlparser) {
212 0 : lt_error_set(&err, LT_ERR_OOM,
213 : "Unable to create an instance of xmlParserCtxt.");
214 0 : goto bail;
215 : }
216 4 : *doc = xmlCtxtReadFile(xmlparser, lt_string_value(regfile), "UTF-8", 0);
217 4 : if (!*doc) {
218 0 : lt_error_set(&err, LT_ERR_FAIL_ON_XML,
219 : "Unable to read the xml file: %s",
220 : lt_string_value(regfile));
221 0 : goto bail;
222 : }
223 4 : lt_mem_add_ref(&xml->parent, *doc,
224 : (lt_destroy_func_t)xmlFreeDoc);
225 :
226 : bail:
227 4 : lt_string_unref(regfile);
228 4 : if (xmlparser)
229 4 : xmlFreeParserCtxt(xmlparser);
230 :
231 4 : if (lt_error_is_set(err, LT_ERR_ANY)) {
232 0 : if (error)
233 0 : *error = lt_error_ref(err);
234 : else
235 0 : lt_error_print(err, LT_ERR_ANY);
236 0 : lt_error_unref(err);
237 :
238 0 : return FALSE;
239 : }
240 :
241 4 : return TRUE;
242 : }
243 :
244 : static lt_bool_t
245 16 : _lt_xml_merge_keys(lt_xml_t *xml,
246 : xmlDocPtr doc1,
247 : xmlDocPtr doc2,
248 : lt_error_t **error)
249 : {
250 16 : xmlXPathContextPtr xctxt = NULL;
251 16 : xmlXPathObjectPtr xobj = NULL;
252 : xmlNodePtr parent_node;
253 : int i, n;
254 16 : lt_bool_t retval = FALSE;
255 :
256 16 : xctxt = xmlXPathNewContext(doc1);
257 16 : if (!xctxt) {
258 0 : lt_error_set(error, LT_ERR_OOM,
259 : "Unable to create an instance of xmlXPathContextPtr");
260 0 : goto bail;
261 : }
262 16 : xobj = xmlXPathEvalExpression((const xmlChar *)"/ldmlBCP47/keyword", xctxt);
263 16 : if (!xobj) {
264 0 : lt_error_set(error, LT_ERR_FAIL_ON_XML,
265 : "No valid elements for %s: keyword",
266 : doc1->name);
267 0 : goto bail;
268 : }
269 16 : if ((n = xmlXPathNodeSetGetLength(xobj->nodesetval)) != 1) {
270 0 : lt_error_set(error, LT_ERR_FAIL_ON_XML,
271 : "Too many keyword elements in %s: %s", doc1->name, doc2->name);
272 0 : goto bail;
273 : }
274 16 : parent_node = xmlXPathNodeSetItem(xobj->nodesetval, 0);
275 16 : xmlXPathFreeObject(xobj);
276 16 : xmlXPathFreeContext(xctxt);
277 16 : xobj = NULL;
278 16 : xctxt = NULL;
279 :
280 16 : xctxt = xmlXPathNewContext(doc2);
281 16 : if (!xctxt) {
282 0 : lt_error_set(error, LT_ERR_OOM,
283 : "Unable to create an instance of xmlXPathContextPtr");
284 0 : goto bail;
285 : }
286 16 : xobj = xmlXPathEvalExpression((const xmlChar *)"/ldmlBCP47/keyword/key", xctxt);
287 16 : if (!xobj) {
288 0 : lt_error_set(error, LT_ERR_FAIL_ON_XML,
289 : "No valid elements for %s: key",
290 : doc2->name);
291 0 : goto bail;
292 : }
293 16 : n = xmlXPathNodeSetGetLength(xobj->nodesetval);
294 32 : for (i = 0; i < n; i++) {
295 16 : xmlNodePtr p = xmlDocCopyNode(xmlXPathNodeSetItem(xobj->nodesetval, i), doc1, 1);
296 :
297 16 : xmlAddChild(parent_node, p);
298 : }
299 :
300 16 : retval = TRUE;
301 : bail:
302 16 : if (xobj)
303 16 : xmlXPathFreeObject(xobj);
304 16 : if (xctxt)
305 16 : xmlXPathFreeContext(xctxt);
306 16 : lt_mem_remove_ref(&xml->parent, doc2);
307 16 : xmlFreeDoc(doc2);
308 :
309 16 : return retval;
310 : }
311 :
312 : /*< public >*/
313 : lt_xml_t *
314 28 : lt_xml_new(void)
315 : {
316 28 : lt_error_t *err = NULL;
317 :
318 : #ifdef _WIN32
319 : HANDLE __lt_xml_lock = CreateMutex(NULL, FALSE, NULL);
320 : #else
321 28 : pthread_mutex_lock(&__lt_xml_lock);
322 : #endif
323 :
324 28 : if (__xml) {
325 : #ifdef _WIN32
326 : ReleaseMutex(__lt_xml_lock);
327 : #else
328 24 : pthread_mutex_unlock(&__lt_xml_lock);
329 : #endif
330 24 : return lt_xml_ref(__xml);
331 : }
332 :
333 4 : __xml = lt_mem_alloc_object(sizeof (lt_xml_t));
334 4 : if (__xml) {
335 4 : xmlDocPtr doc = NULL;
336 :
337 4 : lt_mem_add_weak_pointer(&__xml->parent, (lt_pointer_t *)&__xml);
338 4 : if (!lt_xml_read_subtag_registry(__xml, &err))
339 : goto bail;
340 4 : if (!lt_xml_read_cldr_bcp47(__xml, "calendar.xml",
341 4 : &__xml->cldr_bcp47_calendar,
342 : &err))
343 : goto bail;
344 4 : if (!lt_xml_read_cldr_bcp47(__xml, "collation.xml",
345 4 : &__xml->cldr_bcp47_collation,
346 : &err))
347 : goto bail;
348 4 : if (!lt_xml_read_cldr_bcp47(__xml, "currency.xml",
349 4 : &__xml->cldr_bcp47_currency,
350 : &err))
351 : goto bail;
352 4 : if (!lt_xml_read_cldr_bcp47(__xml, "number.xml",
353 4 : &__xml->cldr_bcp47_number,
354 : &err))
355 : goto bail;
356 4 : if (!lt_xml_read_cldr_bcp47(__xml, "timezone.xml",
357 4 : &__xml->cldr_bcp47_timezone,
358 : &err))
359 : goto bail;
360 4 : if (!lt_xml_read_cldr_bcp47(__xml, "transform.xml",
361 4 : &__xml->cldr_bcp47_transform,
362 : &err))
363 : goto bail;
364 4 : if (!lt_xml_read_cldr_bcp47(__xml, "transform_ime.xml",
365 : &doc,
366 : &err))
367 : goto bail;
368 4 : if (!_lt_xml_merge_keys(__xml, __xml->cldr_bcp47_transform, doc, &err))
369 : goto bail;
370 4 : if (!lt_xml_read_cldr_bcp47(__xml, "transform_keyboard.xml",
371 : &doc,
372 : &err))
373 : goto bail;
374 4 : if (!_lt_xml_merge_keys(__xml, __xml->cldr_bcp47_transform, doc, &err))
375 : goto bail;
376 4 : if (!lt_xml_read_cldr_bcp47(__xml, "transform_mt.xml",
377 : &doc,
378 : &err))
379 : goto bail;
380 4 : if (!_lt_xml_merge_keys(__xml, __xml->cldr_bcp47_transform, doc, &err))
381 : goto bail;
382 4 : if (!lt_xml_read_cldr_bcp47(__xml, "transform_private_use.xml",
383 : &doc,
384 : &err))
385 : goto bail;
386 4 : if (!_lt_xml_merge_keys(__xml, __xml->cldr_bcp47_transform, doc, &err))
387 : goto bail;
388 4 : if (!lt_xml_read_cldr_bcp47(__xml, "variant.xml",
389 4 : &__xml->cldr_bcp47_variant,
390 : &err))
391 : goto bail;
392 4 : if (!lt_xml_read_cldr_supplemental(__xml, "likelySubtags.xml",
393 4 : &__xml->cldr_supplemental_likelysubtags,
394 : &err))
395 : goto bail;
396 : }
397 :
398 : bail:
399 4 : if (lt_error_is_set(err, LT_ERR_ANY)) {
400 0 : lt_error_print(err, LT_ERR_ANY);
401 0 : lt_error_unref(err);
402 0 : lt_xml_unref(__xml);
403 : }
404 :
405 : #ifdef _WIN32
406 : ReleaseMutex(__lt_xml_lock);
407 : #else
408 4 : pthread_mutex_unlock(&__lt_xml_lock);
409 : #endif
410 4 : return __xml;
411 : }
412 :
413 : lt_xml_t *
414 24 : lt_xml_ref(lt_xml_t *xml)
415 : {
416 24 : lt_return_val_if_fail (xml != NULL, NULL);
417 :
418 24 : return lt_mem_ref(&xml->parent);
419 : }
420 :
421 : void
422 28 : lt_xml_unref(lt_xml_t *xml)
423 : {
424 28 : if (xml)
425 28 : lt_mem_unref(&xml->parent);
426 28 : }
427 :
428 : const xmlDocPtr
429 28 : lt_xml_get_subtag_registry(lt_xml_t *xml)
430 : {
431 28 : lt_return_val_if_fail (xml != NULL, NULL);
432 :
433 28 : return xml->subtag_registry;
434 : }
435 :
436 : const xmlDocPtr
437 0 : lt_xml_get_cldr(lt_xml_t *xml,
438 : lt_xml_cldr_t type)
439 : {
440 0 : lt_return_val_if_fail (xml != NULL, NULL);
441 :
442 0 : switch (type) {
443 : case LT_XML_CLDR_BCP47_CALENDAR:
444 0 : return xml->cldr_bcp47_calendar;
445 : case LT_XML_CLDR_BCP47_COLLATION:
446 0 : return xml->cldr_bcp47_collation;
447 : case LT_XML_CLDR_BCP47_CURRENCY:
448 0 : return xml->cldr_bcp47_currency;
449 : case LT_XML_CLDR_BCP47_NUMBER:
450 0 : return xml->cldr_bcp47_number;
451 : case LT_XML_CLDR_BCP47_TIMEZONE:
452 0 : return xml->cldr_bcp47_timezone;
453 : case LT_XML_CLDR_BCP47_TRANSFORM:
454 0 : return xml->cldr_bcp47_transform;
455 : case LT_XML_CLDR_BCP47_VARIANT:
456 0 : return xml->cldr_bcp47_variant;
457 : case LT_XML_CLDR_SUPPLEMENTAL_LIKELY_SUBTAGS:
458 0 : return xml->cldr_supplemental_likelysubtags;
459 : default:
460 0 : break;
461 : }
462 :
463 0 : return NULL;
464 : }
|