1 /*
2 * Copyright (C) 2002-2006 Sergey V. Udaltsov <svu@gnome.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20 #include <errno.h>
21 #include <locale.h>
22 #include <libintl.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/param.h>
26 #include <sys/stat.h>
27
28 #include "config.h"
29
30 #include "xklavier_private.h"
31
32 #include "utf8.c"
33
34 static GObjectClass *parent_class = NULL;
35
36 static xmlXPathCompExprPtr models_xpath;
37 static xmlXPathCompExprPtr layouts_xpath;
38 static xmlXPathCompExprPtr option_groups_xpath;
39
40 static GRegex **xml_encode_regexen = NULL;
41 static GRegex **xml_decode_regexen = NULL;
42 static const char *xml_decode_regexen_str[] = { "<", ">", "&" };
43 static const char *xml_encode_regexen_str[] = { "<", ">", "&" };
44
45 /* gettext domain for translations */
46 #define XKB_DOMAIN "xkeyboard-config"
47
48 enum {
49 PROP_0,
50 PROP_ENGINE
51 };
52
53 typedef struct {
54 gchar **patterns;
55 XklTwoConfigItemsProcessFunc func;
56 gpointer data;
57 gboolean country_matched;
58 gboolean language_matched;
59 const XklConfigItem *layout_item;
60 } SearchParamType;
61
62 static gboolean
xkl_xml_find_config_item_child(xmlNodePtr iptr,xmlNodePtr * ptr)63 xkl_xml_find_config_item_child(xmlNodePtr iptr, xmlNodePtr * ptr)
64 {
65 /*
66 * Walking through the 1st level children of iptr
67 * looking for the configItem
68 */
69 if (iptr->type != XML_ELEMENT_NODE)
70 return FALSE;
71 *ptr = iptr->children;
72 while (*ptr != NULL) {
73 switch ((*ptr)->type) {
74 case XML_ELEMENT_NODE:
75 return !g_ascii_strcasecmp
76 ((char *) (*ptr)->name, "configItem");
77 case XML_TEXT_NODE:
78 case XML_COMMENT_NODE:
79 (*ptr) = (*ptr)->next;
80 continue;
81 default:
82 return FALSE;
83 }
84 break;
85 }
86 return FALSE;
87 }
88
89 static xmlNodePtr
xkl_find_element(xmlNodePtr ptr,const gchar * tag_name)90 xkl_find_element(xmlNodePtr ptr, const gchar * tag_name)
91 {
92 xmlNodePtr found_element = NULL;
93
94 /* Look through all siblings, trying to find a node with proper name */
95 while (ptr != NULL) {
96 char *node_name = (char *) ptr->name;
97 if (ptr->type != XML_TEXT_NODE) {
98 if (!g_ascii_strcasecmp(node_name, tag_name)) {
99 found_element = ptr;
100 break;
101 }
102 }
103 ptr = ptr->next;
104 }
105 return found_element;
106 }
107
108 static gboolean
xkl_item_populate_optional_array(XklConfigItem * item,xmlNodePtr ptr,const gchar list_tag[],const gchar element_tag[],const gchar property_name[])109 xkl_item_populate_optional_array(XklConfigItem * item, xmlNodePtr ptr,
110 const gchar list_tag[],
111 const gchar element_tag[],
112 const gchar property_name[])
113 {
114 xmlNodePtr top_list_element =
115 xkl_find_element(ptr, list_tag), element_ptr;
116 gint n_elements, idx;
117 gchar **elements = NULL;
118
119 if (top_list_element == NULL || top_list_element->children == NULL)
120 return FALSE;
121
122 n_elements = 0;
123
124 /* First, count countries */
125 element_ptr = top_list_element->children;
126 while (NULL !=
127 (element_ptr =
128 xkl_find_element(element_ptr, element_tag))) {
129 n_elements++;
130 element_ptr = element_ptr->next;
131 }
132
133 if (n_elements == 0)
134 return FALSE;
135
136 elements = g_new0(gchar *, n_elements + 1);
137 /* Then, actually, populate the list */
138 element_ptr = top_list_element->children;
139 for (idx = 0;
140 NULL != (element_ptr =
141 xkl_find_element(element_ptr, element_tag));
142 element_ptr = element_ptr->next, idx++) {
143 elements[idx] =
144 g_strdup((const char *) element_ptr->
145 children->content);
146 }
147
148 g_object_set_data_full(G_OBJECT(item),
149 property_name, elements, (GDestroyNotify)
150 g_strfreev);
151 return TRUE;
152 }
153
154 #include "libxml/parserInternals.h"
155
156 gboolean
xkl_read_config_item(XklConfigRegistry * config,gint doc_index,xmlNodePtr iptr,XklConfigItem * item)157 xkl_read_config_item(XklConfigRegistry * config, gint doc_index,
158 xmlNodePtr iptr, XklConfigItem * item)
159 {
160 xmlNodePtr name_element, ptr;
161 xmlNodePtr desc_element = NULL, short_desc_element =
162 NULL, vendor_element = NULL;
163
164 gchar *vendor = NULL, *translated = NULL, *escaped =
165 NULL, *unescaped = NULL;
166
167 guchar *s = NULL;
168
169 gint i;
170
171 *item->name = 0;
172 *item->short_description = 0;
173 *item->description = 0;
174
175 g_object_set_data(G_OBJECT(item), XCI_PROP_VENDOR, NULL);
176 g_object_set_data(G_OBJECT(item), XCI_PROP_COUNTRY_LIST, NULL);
177 g_object_set_data(G_OBJECT(item), XCI_PROP_LANGUAGE_LIST, NULL);
178
179 if (!xkl_xml_find_config_item_child(iptr, &ptr))
180 return FALSE;
181
182 if (doc_index > 0)
183 g_object_set_data(G_OBJECT(item), XCI_PROP_EXTRA_ITEM,
184 GINT_TO_POINTER(TRUE));
185
186 ptr = ptr->children;
187
188 if (ptr->type == XML_TEXT_NODE)
189 ptr = ptr->next;
190 name_element = ptr;
191 ptr = ptr->next;
192
193 short_desc_element = xkl_find_element(ptr, XML_TAG_SHORT_DESCR);
194 desc_element = xkl_find_element(ptr, XML_TAG_DESCR);
195 vendor_element = xkl_find_element(ptr, XML_TAG_VENDOR);
196
197 if (name_element != NULL && name_element->children != NULL) {
198 strncat(item->name,
199 (char *) name_element->children->content,
200 XKL_MAX_CI_NAME_LENGTH - 1);
201
202 s = utf8_check((guchar *)item->name);
203
204 for (i = strlen(item->name); i > 0 && s != NULL; i--) {
205 item->name[i] = 0;
206 s = utf8_check((guchar *)item->name);
207 }
208 }
209
210 if (short_desc_element != NULL
211 && short_desc_element->children != NULL) {
212 strncat(item->short_description,
213 dgettext(XKB_DOMAIN, (const char *)
214 short_desc_element->children->content),
215 XKL_MAX_CI_SHORT_DESC_LENGTH - 1);
216
217 s = utf8_check((guchar *)item->short_description);
218
219 for (i = strlen(item->short_description); i > 0 && s != NULL; i--) {
220 item->short_description[i] = 0;
221 s = utf8_check((guchar *)item->short_description);
222 }
223 }
224
225 if (desc_element != NULL && desc_element->children != NULL) {
226 /* Convert all xml-related characters to XML form, otherwise dgettext won't find the translation
227 * The conversion is not using libxml2, because there are no handy functions in API */
228 translated =
229 g_strdup((gchar *) desc_element->children->content);
230 for (i =
231 sizeof(xml_encode_regexen_str) /
232 sizeof(xml_encode_regexen_str[0]); --i >= 0;) {
233 escaped =
234 g_regex_replace(xml_encode_regexen[i],
235 translated, -1, 0,
236 xml_decode_regexen_str[i], 0,
237 NULL);
238 g_free(translated);
239 translated = escaped;
240 }
241 escaped = translated;
242
243 /* Do the translation! */
244 translated =
245 g_strdup(dgettext(XKB_DOMAIN, (const char *) escaped));
246 g_free(escaped);
247
248 /* Convert all XML entities back to normal form */
249 for (i =
250 sizeof(xml_decode_regexen_str) /
251 sizeof(xml_decode_regexen_str[0]); --i >= 0;) {
252 unescaped =
253 g_regex_replace(xml_decode_regexen[i],
254 translated, -1, 0,
255 xml_encode_regexen_str[i], 0,
256 NULL);
257 g_free(translated);
258 translated = unescaped;
259 }
260
261 strncat(item->description,
262 translated, XKL_MAX_CI_DESC_LENGTH - 1);
263
264 s = utf8_check((guchar *)item->description);
265
266 for (i = strlen(item->description); i > 0 && s != NULL; i--) {
267 item->description[i] = 0;
268 s = utf8_check((guchar *)item->description);
269 }
270
271 g_free(s);
272 g_free(translated);
273 }
274
275 if (vendor_element != NULL && vendor_element->children != NULL) {
276 vendor =
277 g_strdup((const char *) vendor_element->children->
278 content);
279 g_object_set_data_full(G_OBJECT(item), XCI_PROP_VENDOR,
280 vendor, g_free);
281 }
282
283 xkl_item_populate_optional_array(item, ptr, XML_TAG_COUNTRY_LIST,
284 XML_TAG_ISO3166ID,
285 XCI_PROP_COUNTRY_LIST);
286 xkl_item_populate_optional_array(item, ptr, XML_TAG_LANGUAGE_LIST,
287 XML_TAG_ISO639ID,
288 XCI_PROP_LANGUAGE_LIST);
289
290 return TRUE;
291 }
292
293 static void
xkl_config_registry_foreach_in_nodeset(XklConfigRegistry * config,GSList ** processed_ids,gint doc_index,xmlNodeSetPtr nodes,XklConfigItemProcessFunc func,gpointer data)294 xkl_config_registry_foreach_in_nodeset(XklConfigRegistry * config,
295 GSList ** processed_ids,
296 gint doc_index, xmlNodeSetPtr nodes,
297 XklConfigItemProcessFunc func,
298 gpointer data)
299 {
300 gint i;
301 if (nodes != NULL) {
302 xmlNodePtr *pnode = nodes->nodeTab;
303 XklConfigItem *ci = xkl_config_item_new();
304 for (i = nodes->nodeNr; --i >= 0;) {
305 if (xkl_read_config_item
306 (config, doc_index, *pnode, ci)) {
307 if (g_slist_find_custom
308 (*processed_ids, ci->name,
309 (GCompareFunc) g_ascii_strcasecmp) ==
310 NULL) {
311 func(config, ci, data);
312 *processed_ids =
313 g_slist_append(*processed_ids,
314 g_strdup
315 (ci->name));
316 }
317 }
318
319 pnode++;
320 }
321 g_object_unref(G_OBJECT(ci));
322 }
323 }
324
325 void
xkl_config_registry_foreach_in_xpath(XklConfigRegistry * config,xmlXPathCompExprPtr xpath_comp_expr,XklConfigItemProcessFunc func,gpointer data)326 xkl_config_registry_foreach_in_xpath(XklConfigRegistry * config,
327 xmlXPathCompExprPtr
328 xpath_comp_expr,
329 XklConfigItemProcessFunc func,
330 gpointer data)
331 {
332 xmlXPathObjectPtr xpath_obj;
333 gint di;
334 GSList *processed_ids = NULL;
335
336 if (!xkl_config_registry_is_initialized(config))
337 return;
338
339 for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) {
340 xmlXPathContextPtr xmlctxt =
341 xkl_config_registry_priv(config, xpath_contexts[di]);
342 if (xmlctxt == NULL)
343 continue;
344
345 xpath_obj = xmlXPathCompiledEval(xpath_comp_expr, xmlctxt);
346 if (xpath_obj == NULL)
347 continue;
348
349 xkl_config_registry_foreach_in_nodeset(config,
350 &processed_ids, di,
351 xpath_obj->
352 nodesetval, func,
353 data);
354 xmlXPathFreeObject(xpath_obj);
355 }
356 g_slist_foreach(processed_ids, (GFunc) g_free, NULL);
357 g_slist_free(processed_ids);
358 }
359
360 void
xkl_config_registry_foreach_in_xpath_with_param(XklConfigRegistry * config,const gchar * format,const gchar * value,XklConfigItemProcessFunc func,gpointer data)361 xkl_config_registry_foreach_in_xpath_with_param(XklConfigRegistry
362 * config,
363 const gchar *
364 format,
365 const gchar *
366 value,
367 XklConfigItemProcessFunc
368 func, gpointer data)
369 {
370 char xpath_expr[1024];
371 xmlXPathObjectPtr xpath_obj;
372 gint di;
373 GSList *processed_ids = NULL;
374
375 if (!xkl_config_registry_is_initialized(config))
376 return;
377
378 g_snprintf(xpath_expr, sizeof xpath_expr, format, value);
379
380 for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) {
381 xmlXPathContextPtr xmlctxt =
382 xkl_config_registry_priv(config, xpath_contexts[di]);
383 if (xmlctxt == NULL)
384 continue;
385
386 xpath_obj =
387 xmlXPathEval((unsigned char *) xpath_expr, xmlctxt);
388 if (xpath_obj == NULL)
389 continue;
390
391 xkl_config_registry_foreach_in_nodeset(config,
392 &processed_ids, di,
393 xpath_obj->
394 nodesetval, func,
395 data);
396 xmlXPathFreeObject(xpath_obj);
397 }
398 g_slist_foreach(processed_ids, (GFunc) g_free, NULL);
399 g_slist_free(processed_ids);
400 }
401
402 static gboolean
xkl_config_registry_find_object(XklConfigRegistry * config,const gchar * format,const gchar * arg1,XklConfigItem * pitem,xmlNodePtr * pnode)403 xkl_config_registry_find_object(XklConfigRegistry * config,
404 const gchar * format,
405 const gchar * arg1,
406 XklConfigItem * pitem /* in/out */ ,
407 xmlNodePtr * pnode /* out */ )
408 {
409 xmlXPathObjectPtr xpath_obj;
410 xmlNodeSetPtr nodes;
411 gboolean rv = FALSE;
412 gchar xpath_expr[1024];
413 gint di;
414
415 if (!xkl_config_registry_is_initialized(config))
416 return FALSE;
417
418 g_snprintf(xpath_expr, sizeof xpath_expr, format, arg1,
419 pitem->name);
420
421 for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) {
422 xmlXPathContextPtr xmlctxt =
423 xkl_config_registry_priv(config, xpath_contexts[di]);
424 if (xmlctxt == NULL)
425 continue;
426
427 xpath_obj =
428 xmlXPathEval((unsigned char *) xpath_expr, xmlctxt);
429 if (xpath_obj == NULL)
430 continue;
431
432 nodes = xpath_obj->nodesetval;
433 if (nodes != NULL && nodes->nodeTab != NULL
434 && nodes->nodeNr > 0) {
435 rv = xkl_read_config_item(config, di,
436 nodes->nodeTab[0],
437 pitem);
438 if (pnode != NULL) {
439 *pnode = *nodes->nodeTab;
440 }
441 }
442
443 xmlXPathFreeObject(xpath_obj);
444 }
445 return rv;
446 }
447
448 gchar *
xkl_config_rec_merge_layouts(const XklConfigRec * data)449 xkl_config_rec_merge_layouts(const XklConfigRec * data)
450 {
451 return xkl_strings_concat_comma_separated(data->layouts);
452 }
453
454 gchar *
xkl_config_rec_merge_variants(const XklConfigRec * data)455 xkl_config_rec_merge_variants(const XklConfigRec * data)
456 {
457 return xkl_strings_concat_comma_separated(data->variants);
458 }
459
460 gchar *
xkl_config_rec_merge_options(const XklConfigRec * data)461 xkl_config_rec_merge_options(const XklConfigRec * data)
462 {
463 return xkl_strings_concat_comma_separated(data->options);
464 }
465
466 gchar *
xkl_strings_concat_comma_separated(gchar ** array)467 xkl_strings_concat_comma_separated(gchar ** array)
468 {
469 if (array) {
470 return g_strjoinv(",", array);
471 } else {
472 return g_strdup("");
473 }
474 }
475
476 void
xkl_config_rec_split_layouts(XklConfigRec * data,const gchar * merged)477 xkl_config_rec_split_layouts(XklConfigRec * data, const gchar * merged)
478 {
479 xkl_strings_split_comma_separated(&data->layouts, merged);
480 }
481
482 void
xkl_config_rec_split_variants(XklConfigRec * data,const gchar * merged)483 xkl_config_rec_split_variants(XklConfigRec * data, const gchar * merged)
484 {
485 xkl_strings_split_comma_separated(&data->variants, merged);
486 }
487
488 void
xkl_config_rec_split_options(XklConfigRec * data,const gchar * merged)489 xkl_config_rec_split_options(XklConfigRec * data, const gchar * merged)
490 {
491 xkl_strings_split_comma_separated(&data->options, merged);
492 }
493
494 void
xkl_strings_split_comma_separated(gchar *** array,const gchar * merged)495 xkl_strings_split_comma_separated(gchar *** array, const gchar * merged)
496 {
497 *array = g_strsplit(merged, ",", 0);
498 }
499
500 gchar *
xkl_engine_get_ruleset_name(XklEngine * engine,const gchar default_ruleset[])501 xkl_engine_get_ruleset_name(XklEngine * engine,
502 const gchar default_ruleset[])
503 {
504 static gchar rules_set_name[1024] = "";
505 if (!rules_set_name[0]) {
506 /* first call */
507 gchar *rf = NULL;
508 if (!xkl_config_rec_get_from_root_window_property
509 (NULL,
510 xkl_engine_priv(engine, base_config_atom),
511 &rf, engine) || (rf == NULL)) {
512 g_strlcpy(rules_set_name, default_ruleset,
513 sizeof rules_set_name);
514 xkl_debug(100,
515 "Using default rules set: [%s]\n",
516 rules_set_name);
517 return rules_set_name;
518 }
519 g_strlcpy(rules_set_name, rf, sizeof rules_set_name);
520 g_free(rf);
521 }
522 xkl_debug(100, "Rules set: [%s]\n", rules_set_name);
523 return rules_set_name;
524 }
525
526 XklConfigRegistry *
xkl_config_registry_get_instance(XklEngine * engine)527 xkl_config_registry_get_instance(XklEngine * engine)
528 {
529 XklConfigRegistry *config;
530
531 if (!engine) {
532 xkl_debug(10,
533 "xkl_config_registry_get_instance : engine is NULL ?\n");
534 return NULL;
535 }
536
537 config =
538 XKL_CONFIG_REGISTRY(g_object_new
539 (xkl_config_registry_get_type(),
540 "engine", engine, NULL));
541
542 return config;
543 }
544
545 /* We process descriptions as "leaf" elements - this is ok for base.xml*/
546 gboolean
xkl_config_registry_load_from_file(XklConfigRegistry * config,const gchar * file_name,gint docidx)547 xkl_config_registry_load_from_file(XklConfigRegistry * config,
548 const gchar * file_name, gint docidx)
549 {
550 xmlParserCtxtPtr ctxt = xmlNewParserCtxt();
551 xmlDocPtr doc;
552
553 xkl_debug(100, "Loading XML registry from file %s\n", file_name);
554
555 xmlSAX2InitDefaultSAXHandler(ctxt->sax, TRUE);
556
557 doc = xkl_config_registry_priv(config, docs[docidx]) =
558 xmlCtxtReadFile(ctxt, file_name, NULL, XML_PARSE_NOBLANKS);
559 xmlFreeParserCtxt(ctxt);
560
561 if (doc == NULL) {
562 xkl_config_registry_priv(config, xpath_contexts[docidx]) =
563 NULL;
564 xkl_last_error_message =
565 "Could not parse primary XKB configuration registry";
566 return FALSE;
567 }
568
569 xkl_config_registry_priv(config, xpath_contexts[docidx]) =
570 xmlXPathNewContext(doc);
571
572 return TRUE;
573 }
574
575 gboolean
xkl_config_registry_load_helper(XklConfigRegistry * config,const char default_ruleset[],const char base_dir[],gboolean if_extras_needed)576 xkl_config_registry_load_helper(XklConfigRegistry * config,
577 const char default_ruleset[],
578 const char base_dir[],
579 gboolean if_extras_needed)
580 {
581 struct stat stat_buf;
582 gchar file_name[MAXPATHLEN] = "";
583 XklEngine *engine = xkl_config_registry_get_engine(config);
584 gchar *rf = xkl_engine_get_ruleset_name(engine, default_ruleset);
585
586 if (rf == NULL || rf[0] == '\0')
587 return FALSE;
588
589 g_snprintf(file_name, sizeof file_name, "%s/%s.xml", base_dir, rf);
590
591 if (stat(file_name, &stat_buf) != 0) {
592 xkl_debug(0, "Missing registry file %s\n", file_name);
593 xkl_last_error_message = "Missing registry file";
594 return FALSE;
595 }
596
597 if (!xkl_config_registry_load_from_file(config, file_name, 0))
598 return FALSE;
599
600 if (!if_extras_needed)
601 return TRUE;
602
603 g_snprintf(file_name, sizeof file_name, "%s/%s.extras.xml",
604 base_dir, rf);
605
606 /* no extras - ok, no problem */
607 if (stat(file_name, &stat_buf) != 0)
608 return TRUE;
609
610 return xkl_config_registry_load_from_file(config, file_name, 1);
611 }
612
613 void
xkl_config_registry_free(XklConfigRegistry * config)614 xkl_config_registry_free(XklConfigRegistry * config)
615 {
616 if (xkl_config_registry_is_initialized(config)) {
617 gint di;
618 for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) {
619 xmlXPathContextPtr xmlctxt =
620 xkl_config_registry_priv(config,
621 xpath_contexts[di]);
622 if (xmlctxt == NULL)
623 continue;
624
625 xmlXPathFreeContext(xmlctxt);
626 xmlFreeDoc(xkl_config_registry_priv
627 (config, docs[di]));
628 xkl_config_registry_priv(config,
629 xpath_contexts[di]) =
630 NULL;
631 xkl_config_registry_priv(config, docs[di]) = NULL;
632 }
633
634 }
635 }
636
637 void
xkl_config_registry_foreach_model(XklConfigRegistry * config,XklConfigItemProcessFunc func,gpointer data)638 xkl_config_registry_foreach_model(XklConfigRegistry * config,
639 XklConfigItemProcessFunc func,
640 gpointer data)
641 {
642 xkl_config_registry_foreach_in_xpath(config, models_xpath,
643 func, data);
644 }
645
646 void
xkl_config_registry_foreach_layout(XklConfigRegistry * config,XklConfigItemProcessFunc func,gpointer data)647 xkl_config_registry_foreach_layout(XklConfigRegistry * config,
648 XklConfigItemProcessFunc func,
649 gpointer data)
650 {
651 xkl_config_registry_foreach_in_xpath(config, layouts_xpath,
652 func, data);
653 }
654
655 void
xkl_config_registry_foreach_layout_variant(XklConfigRegistry * config,const gchar * layout_name,XklConfigItemProcessFunc func,gpointer data)656 xkl_config_registry_foreach_layout_variant(XklConfigRegistry *
657 config,
658 const gchar *
659 layout_name,
660 XklConfigItemProcessFunc
661 func, gpointer data)
662 {
663 xkl_config_registry_foreach_in_xpath_with_param(config,
664 XKBCR_VARIANT_PATH
665 "[../../configItem/name = '%s']",
666 layout_name,
667 func, data);
668 }
669
670 void
xkl_config_registry_foreach_option_group(XklConfigRegistry * config,XklConfigItemProcessFunc func,gpointer data)671 xkl_config_registry_foreach_option_group(XklConfigRegistry *
672 config,
673 XklConfigItemProcessFunc
674 func, gpointer data)
675 {
676 xmlXPathObjectPtr xpath_obj;
677 gint di, j;
678 GSList *processed_ids = NULL;
679
680 if (!xkl_config_registry_is_initialized(config))
681 return;
682
683 for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) {
684 xmlNodeSetPtr nodes;
685 xmlNodePtr *pnode;
686 XklConfigItem *ci;
687
688 xmlXPathContextPtr xmlctxt =
689 xkl_config_registry_priv(config, xpath_contexts[di]);
690 if (xmlctxt == NULL)
691 continue;
692
693 xpath_obj =
694 xmlXPathCompiledEval(option_groups_xpath, xmlctxt);
695 if (xpath_obj == NULL)
696 continue;
697
698 nodes = xpath_obj->nodesetval;
699 pnode = nodes->nodeTab;
700 ci = xkl_config_item_new();
701 for (j = nodes->nodeNr; --j >= 0;) {
702
703 if (xkl_read_config_item(config, di, *pnode, ci)) {
704 if (g_slist_find_custom
705 (processed_ids, ci->name,
706 (GCompareFunc) g_ascii_strcasecmp) ==
707 NULL) {
708 gboolean allow_multisel = TRUE;
709 xmlChar *sallow_multisel =
710 xmlGetProp(*pnode,
711 (unsigned char *)
712 XCI_PROP_ALLOW_MULTIPLE_SELECTION);
713 if (sallow_multisel != NULL) {
714 allow_multisel =
715 !g_ascii_strcasecmp
716 ("true", (char *)
717 sallow_multisel);
718 xmlFree(sallow_multisel);
719 g_object_set_data(G_OBJECT
720 (ci),
721 XCI_PROP_ALLOW_MULTIPLE_SELECTION,
722 GINT_TO_POINTER
723 (allow_multisel));
724 }
725
726 func(config, ci, data);
727 processed_ids =
728 g_slist_append(processed_ids,
729 g_strdup
730 (ci->name));
731 }
732 }
733
734 pnode++;
735 }
736 g_object_unref(G_OBJECT(ci));
737 xmlXPathFreeObject(xpath_obj);
738 }
739 g_slist_foreach(processed_ids, (GFunc) g_free, NULL);
740 g_slist_free(processed_ids);
741 }
742
743 void
xkl_config_registry_foreach_option(XklConfigRegistry * config,const gchar * option_group_name,XklConfigItemProcessFunc func,gpointer data)744 xkl_config_registry_foreach_option(XklConfigRegistry * config,
745 const gchar *
746 option_group_name,
747 XklConfigItemProcessFunc func,
748 gpointer data)
749 {
750 xkl_config_registry_foreach_in_xpath_with_param(config,
751 XKBCR_OPTION_PATH
752 "[../configItem/name = '%s']",
753 option_group_name,
754 func, data);
755 }
756
757 static gboolean
search_all(const gchar * haystack,gchar ** needles)758 search_all(const gchar * haystack, gchar ** needles)
759 {
760 /* match anything */
761 if (!needles || !*needles)
762 return TRUE;
763
764 gchar *uchs = g_utf8_strup(haystack, -1);
765 do {
766 if (g_strstr_len(uchs, -1, *needles) == NULL) {
767 g_free(uchs);
768 return FALSE;
769 }
770 needles++;
771 } while (*needles);
772
773 g_free(uchs);
774 return TRUE;
775 }
776
777 static gboolean
if_country_matches_pattern(const XklConfigItem * item,gchar ** patterns,const gboolean check_name)778 if_country_matches_pattern(const XklConfigItem * item,
779 gchar ** patterns, const gboolean check_name)
780 {
781 const gchar *country_desc;
782 if (check_name) {
783 gchar *upper_name = g_ascii_strup(item->name, -1);
784 country_desc = xkl_get_country_name(upper_name);
785 g_free(upper_name);
786 xkl_debug(200, "Checking layout country: [%s]\n",
787 country_desc);
788 if ((country_desc != NULL)
789 && search_all(country_desc, patterns))
790 return TRUE;
791 }
792
793 gchar **countries = g_object_get_data(G_OBJECT(item),
794 XCI_PROP_COUNTRY_LIST);
795 for (; countries && *countries; countries++) {
796 country_desc = xkl_get_country_name(*countries);
797 xkl_debug(200, "Checking country: [%s][%s]\n",
798 *countries, country_desc);
799 if ((country_desc != NULL)
800 && search_all(country_desc, patterns)) {
801 return TRUE;
802 }
803 }
804 return FALSE;
805 }
806
807 static gboolean
if_language_matches_pattern(const XklConfigItem * item,gchar ** patterns,const gboolean check_name)808 if_language_matches_pattern(const XklConfigItem * item,
809 gchar ** patterns, const gboolean check_name)
810 {
811 const gchar *language_desc;
812 if (check_name) {
813 language_desc = xkl_get_language_name(item->name);
814 xkl_debug(200, "Checking layout language: [%s]\n",
815 language_desc);
816 if ((language_desc != NULL)
817 && search_all(language_desc, patterns))
818 return TRUE;
819 }
820 gchar **languages = g_object_get_data(G_OBJECT(item),
821 XCI_PROP_LANGUAGE_LIST);
822 for (; languages && *languages; languages++) {
823 language_desc = xkl_get_language_name(*languages);
824 xkl_debug(200, "Checking language: [%s][%s]\n",
825 *languages, language_desc);
826 if ((language_desc != NULL)
827 && search_all(language_desc, patterns)) {
828 return TRUE;
829 }
830 }
831 return FALSE;
832 }
833
834 static void
xkl_config_registry_search_by_pattern_in_variant(XklConfigRegistry * config,const XklConfigItem * item,SearchParamType * search_param)835 xkl_config_registry_search_by_pattern_in_variant(XklConfigRegistry *
836 config,
837 const XklConfigItem *
838 item,
839 SearchParamType *
840 search_param)
841 {
842 gboolean variant_matched = FALSE;
843 gchar *full_desc = g_strdup_printf("%s - %s",
844 search_param->
845 layout_item->description,
846 item->description);
847
848 xkl_debug(200, "Variant to check: [%s][%s]\n", item->name,
849 item->description);
850
851 if (search_all(full_desc, search_param->patterns))
852 variant_matched = TRUE;
853
854 g_free(full_desc);
855
856 if (!variant_matched) {
857 gchar **countries = g_object_get_data(G_OBJECT(item),
858 XCI_PROP_COUNTRY_LIST);
859 if (countries && g_strv_length(countries) > 0) {
860 if (if_country_matches_pattern
861 (item, search_param->patterns, FALSE))
862 variant_matched = TRUE;
863 } else {
864 if (search_param->country_matched)
865 variant_matched = TRUE;
866 }
867 }
868
869 if (!variant_matched) {
870 gchar **languages = g_object_get_data(G_OBJECT(item),
871 XCI_PROP_LANGUAGE_LIST);
872 if (languages && g_strv_length(languages) > 0) {
873 if (if_language_matches_pattern
874 (item, search_param->patterns, FALSE))
875 variant_matched = TRUE;
876 } else {
877 if (search_param->language_matched)
878 variant_matched = TRUE;
879 }
880 }
881
882 if (variant_matched)
883 (search_param->func) (config, search_param->layout_item,
884 item, search_param->data);
885 }
886
887 static void
xkl_config_registry_search_by_pattern_in_layout(XklConfigRegistry * config,const XklConfigItem * item,SearchParamType * search_param)888 xkl_config_registry_search_by_pattern_in_layout(XklConfigRegistry * config,
889 const XklConfigItem * item,
890 SearchParamType *
891 search_param)
892 {
893 gchar *upper_name = g_ascii_strup(item->name, -1);
894
895 xkl_debug(200, "Layout to check: [%s][%s]\n", item->name,
896 item->description);
897
898 search_param->country_matched =
899 search_param->language_matched = FALSE;
900
901 if (if_country_matches_pattern(item, search_param->patterns, TRUE))
902 search_param->country_matched = TRUE;
903 else if (if_language_matches_pattern
904 (item, search_param->patterns, TRUE))
905 search_param->language_matched = TRUE;
906 else if (search_all(item->description, search_param->patterns))
907 search_param->language_matched = TRUE;
908
909 if (search_param->country_matched
910 || search_param->language_matched)
911 (search_param->func) (config, item, NULL,
912 search_param->data);
913
914 search_param->layout_item = item;
915
916 xkl_config_registry_foreach_layout_variant(config, item->name,
917 (XklConfigItemProcessFunc)
918 xkl_config_registry_search_by_pattern_in_variant,
919 search_param);
920
921 g_free(upper_name);
922 }
923
924 void
xkl_config_registry_search_by_pattern(XklConfigRegistry * config,const gchar * pattern,XklTwoConfigItemsProcessFunc func,gpointer data)925 xkl_config_registry_search_by_pattern(XklConfigRegistry
926 * config,
927 const gchar *
928 pattern,
929 XklTwoConfigItemsProcessFunc
930 func, gpointer data)
931 {
932 xkl_debug(200, "Searching by pattern: [%s]\n", pattern);
933 gchar *upattern = pattern ? g_utf8_strup(pattern, -1) : NULL;
934 gchar **patterns = pattern ? g_strsplit(upattern, " ", -1) : NULL;
935 SearchParamType search_param = {
936 patterns, func, data
937 };
938 xkl_config_registry_foreach_layout(config, (XklConfigItemProcessFunc)
939 xkl_config_registry_search_by_pattern_in_layout,
940 &search_param);
941 g_strfreev(patterns);
942 g_free(upattern);
943 }
944
945 gboolean
xkl_config_registry_find_model(XklConfigRegistry * config,XklConfigItem * pitem)946 xkl_config_registry_find_model(XklConfigRegistry *
947 config, XklConfigItem * pitem /* in/out */ )
948 {
949 return xkl_config_registry_find_object(config,
950 XKBCR_MODEL_PATH
951 "[configItem/name = '%s%s']",
952 "", pitem, NULL);
953 }
954
955 gboolean
xkl_config_registry_find_layout(XklConfigRegistry * config,XklConfigItem * pitem)956 xkl_config_registry_find_layout(XklConfigRegistry *
957 config,
958 XklConfigItem * pitem /* in/out */ )
959 {
960 return xkl_config_registry_find_object(config,
961 XKBCR_LAYOUT_PATH
962 "[configItem/name = '%s%s']",
963 "", pitem, NULL);
964 }
965
966 gboolean
xkl_config_registry_find_variant(XklConfigRegistry * config,const char * layout_name,XklConfigItem * pitem)967 xkl_config_registry_find_variant(XklConfigRegistry *
968 config,
969 const char
970 *layout_name,
971 XklConfigItem * pitem /* in/out */ )
972 {
973 return xkl_config_registry_find_object(config,
974 XKBCR_VARIANT_PATH
975 "[../../configItem/name = '%s' and configItem/name = '%s']",
976 layout_name, pitem, NULL);
977 }
978
979 gboolean
xkl_config_registry_find_option_group(XklConfigRegistry * config,XklConfigItem * pitem)980 xkl_config_registry_find_option_group(XklConfigRegistry * config, XklConfigItem * pitem /* in/out */
981 )
982 {
983 xmlNodePtr node = NULL;
984 gboolean rv = xkl_config_registry_find_object(config,
985 XKBCR_GROUP_PATH
986 "[configItem/name = '%s%s']",
987 "", pitem, &node);
988 if (rv) {
989 xmlChar *val = xmlGetProp(node, (unsigned char *)
990 XCI_PROP_ALLOW_MULTIPLE_SELECTION);
991 if (val != NULL) {
992 gboolean allow_multisel =
993 !g_ascii_strcasecmp("true",
994 (char *) val);
995 g_object_set_data(G_OBJECT(pitem),
996 XCI_PROP_ALLOW_MULTIPLE_SELECTION,
997 GINT_TO_POINTER(allow_multisel));
998 xmlFree(val);
999 }
1000 }
1001 return rv;
1002 }
1003
1004 gboolean
xkl_config_registry_find_option(XklConfigRegistry * config,const char * option_group_name,XklConfigItem * pitem)1005 xkl_config_registry_find_option(XklConfigRegistry *
1006 config,
1007 const char
1008 *option_group_name,
1009 XklConfigItem * pitem /* in/out */ )
1010 {
1011 return xkl_config_registry_find_object(config,
1012 XKBCR_OPTION_PATH
1013 "[../configItem/name = '%s' and configItem/name = '%s']",
1014 option_group_name,
1015 pitem, NULL);
1016 }
1017
1018 /*
1019 * Calling through vtable
1020 */
1021 gboolean
xkl_config_rec_activate(const XklConfigRec * data,XklEngine * engine)1022 xkl_config_rec_activate(const XklConfigRec * data, XklEngine * engine)
1023 {
1024 xkl_engine_ensure_vtable_inited(engine);
1025 return xkl_engine_vcall(engine,
1026 activate_config_rec) (engine, data);
1027 }
1028
1029 gboolean
xkl_config_registry_load(XklConfigRegistry * config,gboolean if_extras_needed)1030 xkl_config_registry_load(XklConfigRegistry * config,
1031 gboolean if_extras_needed)
1032 {
1033 XklEngine *engine;
1034 xkl_config_registry_free(config);
1035 engine = xkl_config_registry_get_engine(config);
1036 xkl_engine_ensure_vtable_inited(engine);
1037 return xkl_engine_vcall(engine,
1038 load_config_registry) (config,
1039 if_extras_needed);
1040 }
1041
1042 gboolean
xkl_config_rec_write_to_file(XklEngine * engine,const gchar * file_name,const XklConfigRec * data,const gboolean binary)1043 xkl_config_rec_write_to_file(XklEngine * engine,
1044 const gchar * file_name,
1045 const XklConfigRec * data,
1046 const gboolean binary)
1047 {
1048 if ((!binary
1049 && !(xkl_engine_priv(engine, features) &
1050 XKLF_CAN_OUTPUT_CONFIG_AS_ASCII))
1051 || (binary
1052 && !(xkl_engine_priv(engine, features) &
1053 XKLF_CAN_OUTPUT_CONFIG_AS_BINARY))) {
1054 xkl_last_error_message =
1055 "Function not supported at backend";
1056 return FALSE;
1057 }
1058 xkl_engine_ensure_vtable_inited(engine);
1059 return xkl_engine_vcall(engine, write_config_rec_to_file)
1060 (engine, file_name, data, binary);
1061 }
1062
1063 void
xkl_config_rec_dump(FILE * file,XklConfigRec * data)1064 xkl_config_rec_dump(FILE * file, XklConfigRec * data)
1065 {
1066 int j;
1067 fprintf(file, " model: [%s]\n", data->model);
1068 fprintf(file, " layouts:\n");
1069 #define OUTPUT_ARRZ(arrz) \
1070 { \
1071 gchar **p = data->arrz; \
1072 fprintf( file, " " #arrz ":\n" ); \
1073 if ( p != NULL ) \
1074 for( j = 0; *p != NULL; ) \
1075 fprintf( file, " %d: [%s]\n", j++, *p++ ); \
1076 }
1077 OUTPUT_ARRZ(layouts);
1078 OUTPUT_ARRZ(variants);
1079 OUTPUT_ARRZ(options);
1080 }
1081
G_DEFINE_TYPE(XklConfigRegistry,xkl_config_registry,G_TYPE_OBJECT)1082 G_DEFINE_TYPE(XklConfigRegistry, xkl_config_registry, G_TYPE_OBJECT)
1083 static GObject *
1084 xkl_config_registry_constructor(GType type,
1085 guint
1086 n_construct_properties,
1087 GObjectConstructParam *
1088 construct_properties)
1089 {
1090 GObject *obj;
1091 XklConfigRegistry *config;
1092 XklEngine *engine; {
1093 /* Invoke parent constructor. */
1094 g_type_class_peek(XKL_TYPE_CONFIG_REGISTRY);
1095 obj =
1096 parent_class->constructor(type,
1097 n_construct_properties,
1098 construct_properties);
1099 }
1100
1101 config = XKL_CONFIG_REGISTRY(obj);
1102 engine = XKL_ENGINE(g_value_peek_pointer
1103 (construct_properties[0].value));
1104 xkl_config_registry_get_engine(config) = engine;
1105 xkl_engine_ensure_vtable_inited(engine);
1106 xkl_engine_vcall(engine, init_config_registry) (config);
1107 return obj;
1108 }
1109
1110 static void
xkl_config_registry_init(XklConfigRegistry * config)1111 xkl_config_registry_init(XklConfigRegistry * config)
1112 {
1113 config->priv = g_new0(XklConfigRegistryPrivate, 1);
1114 }
1115
1116 static void
xkl_config_registry_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1117 xkl_config_registry_set_property(GObject * object,
1118 guint property_id,
1119 const GValue * value, GParamSpec * pspec)
1120 {
1121 }
1122
1123 static void
xkl_config_registry_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1124 xkl_config_registry_get_property(GObject * object,
1125 guint property_id,
1126 GValue * value, GParamSpec * pspec)
1127 {
1128 XklConfigRegistry *config = XKL_CONFIG_REGISTRY(object);
1129 switch (property_id) {
1130 case PROP_ENGINE:
1131 g_value_set_pointer(value,
1132 xkl_config_registry_get_engine
1133 (config));
1134 break;
1135 }
1136
1137 }
1138
1139 static void
xkl_config_registry_finalize(GObject * obj)1140 xkl_config_registry_finalize(GObject * obj)
1141 {
1142 XklConfigRegistry *config = (XklConfigRegistry *) obj;
1143 xkl_config_registry_free(config);
1144 g_free(config->priv);
1145 G_OBJECT_CLASS(parent_class)->finalize(obj);
1146 }
1147
1148 /*
1149 * This function is actually NEVER called.
1150 * It is 'extern' just to avoid the compilation warnings
1151 * TODO: add class cleanup
1152 */
1153 extern void
xkl_config_registry_class_term(XklConfigRegistryClass * klass)1154 xkl_config_registry_class_term(XklConfigRegistryClass * klass)
1155 {
1156 gint i;
1157 if (models_xpath != NULL) {
1158 xmlXPathFreeCompExpr(models_xpath);
1159 models_xpath = NULL;
1160 }
1161 if (layouts_xpath != NULL) {
1162 xmlXPathFreeCompExpr(layouts_xpath);
1163 layouts_xpath = NULL;
1164 }
1165 if (option_groups_xpath != NULL) {
1166 xmlXPathFreeCompExpr(option_groups_xpath);
1167 option_groups_xpath = NULL;
1168 }
1169 if (xml_encode_regexen != NULL) {
1170 for (i =
1171 sizeof(xml_encode_regexen_str) /
1172 sizeof(xml_encode_regexen_str[0]); --i >= 0;) {
1173 g_regex_unref(xml_encode_regexen[i]);
1174 }
1175 g_free(xml_encode_regexen);
1176 xml_encode_regexen = NULL;
1177 }
1178 if (xml_decode_regexen != NULL) {
1179 for (i =
1180 sizeof(xml_decode_regexen_str) /
1181 sizeof(xml_decode_regexen_str[0]); --i >= 0;) {
1182 g_regex_unref(xml_decode_regexen[i]);
1183 }
1184 g_free(xml_decode_regexen);
1185 xml_decode_regexen = NULL;
1186 }
1187 }
1188
1189 static void
xkl_config_registry_class_init(XklConfigRegistryClass * klass)1190 xkl_config_registry_class_init(XklConfigRegistryClass * klass)
1191 {
1192 GObjectClass *object_class;
1193 GParamSpec *engine_param_spec;
1194 gint i;
1195 object_class = (GObjectClass *) klass;
1196 parent_class = g_type_class_peek_parent(object_class);
1197 object_class->constructor = xkl_config_registry_constructor;
1198 object_class->finalize = xkl_config_registry_finalize;
1199 object_class->set_property = xkl_config_registry_set_property;
1200 object_class->get_property = xkl_config_registry_get_property;
1201 bind_textdomain_codeset(XKB_DOMAIN, "UTF-8");
1202 engine_param_spec =
1203 g_param_spec_object("engine", "Engine", "XklEngine",
1204 XKL_TYPE_ENGINE,
1205 G_PARAM_CONSTRUCT_ONLY |
1206 G_PARAM_READWRITE);
1207 g_object_class_install_property(object_class, PROP_ENGINE,
1208 engine_param_spec);
1209 /* static stuff initialized */
1210 xmlXPathInit();
1211 models_xpath = xmlXPathCompile((unsigned char *)
1212 XKBCR_MODEL_PATH);
1213 layouts_xpath = xmlXPathCompile((unsigned char *)
1214 XKBCR_LAYOUT_PATH);
1215 option_groups_xpath = xmlXPathCompile((unsigned char *)
1216 XKBCR_GROUP_PATH);
1217 xml_encode_regexen =
1218 g_new0(GRegex *,
1219 sizeof(xml_encode_regexen_str) /
1220 sizeof(xml_encode_regexen_str[0]));
1221 xml_decode_regexen =
1222 g_new0(GRegex *,
1223 sizeof(xml_decode_regexen_str) /
1224 sizeof(xml_decode_regexen_str[0]));
1225 for (i =
1226 sizeof(xml_encode_regexen_str) /
1227 sizeof(xml_encode_regexen_str[0]); --i >= 0;) {
1228 xml_encode_regexen[i] =
1229 g_regex_new(xml_encode_regexen_str[i], 0, 0, NULL);
1230 xml_decode_regexen[i] =
1231 g_regex_new(xml_decode_regexen_str[i], 0, 0, NULL);
1232 }
1233 }
1234