2 * Copyright © 2020 Red Hat, Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
32 #include <libxml/parser.h>
34 #include "xkbcommon/xkbregistry.h"
36 #include "util-list.h"
40 typedef void (*destroy_func_t)(struct rxkb_object *object);
43 * All our objects are refcounted and are linked to iterate through them.
44 * Abstract those bits away into a shared parent class so we can generate
45 * most of the functions through macros.
48 struct rxkb_object *parent;
51 destroy_func_t destroy;
54 struct rxkb_iso639_code {
55 struct rxkb_object base;
59 struct rxkb_iso3166_code {
60 struct rxkb_object base;
71 struct rxkb_object base;
72 enum context_state context_state;
74 bool load_extra_rules_files;
75 bool use_secure_getenv;
77 struct list models; /* list of struct rxkb_models */
78 struct list layouts; /* list of struct rxkb_layouts */
79 struct list option_groups; /* list of struct rxkb_option_group */
81 darray(char *) includes;
84 ATTR_PRINTF(3, 0) void (*log_fn)(struct rxkb_context *ctx,
85 enum rxkb_log_level level,
86 const char *fmt, va_list args);
87 enum rxkb_log_level log_level;
93 struct rxkb_object base;
98 enum rxkb_popularity popularity;
102 struct rxkb_object base;
108 enum rxkb_popularity popularity;
110 struct list iso639s; /* list of struct rxkb_iso639_code */
111 struct list iso3166s; /* list of struct rxkb_iso3166_code */
114 struct rxkb_option_group {
115 struct rxkb_object base;
118 struct list options; /* list of struct rxkb_options */
121 enum rxkb_popularity popularity;
125 struct rxkb_object base;
130 enum rxkb_popularity popularity;
134 parse(struct rxkb_context *ctx, const char *path,
135 enum rxkb_popularity popularity);
139 rxkb_log(struct rxkb_context *ctx, enum rxkb_log_level level,
140 const char *fmt, ...)
144 if (ctx->log_level < level)
148 ctx->log_fn(ctx, level, fmt, args);
153 * The format is not part of the argument list in order to avoid the
154 * "ISO C99 requires rest arguments to be used" warning when only the
155 * format is supplied without arguments. Not supplying it would still
156 * result in an error, though.
158 #define log_dbg(ctx, ...) \
159 rxkb_log((ctx), RXKB_LOG_LEVEL_DEBUG, __VA_ARGS__)
160 #define log_info(ctx, ...) \
161 rxkb_log((ctx), RXKB_LOG_LEVEL_INFO, __VA_ARGS__)
162 #define log_warn(ctx, ...) \
163 rxkb_log((ctx), RXKB_LOG_LEVEL_WARNING, __VA_ARGS__)
164 #define log_err(ctx, ...) \
165 rxkb_log((ctx), RXKB_LOG_LEVEL_ERROR, __VA_ARGS__)
166 #define log_wsgo(ctx, ...) \
167 rxkb_log((ctx), RXKB_LOG_LEVEL_CRITICAL, __VA_ARGS__)
170 #define DECLARE_REF_UNREF_FOR_TYPE(type_) \
171 XKB_EXPORT struct type_ * type_##_ref(struct type_ *object) { \
172 rxkb_object_ref(&object->base); \
175 XKB_EXPORT struct type_ * type_##_unref(struct type_ *object) { \
176 if (!object) return NULL; \
177 return rxkb_object_unref(&object->base); \
180 #define DECLARE_CREATE_FOR_TYPE(type_) \
181 static inline struct type_ * type_##_create(struct rxkb_object *parent) { \
182 struct type_ *t = calloc(1, sizeof *t); \
184 rxkb_object_init(&t->base, parent, (destroy_func_t)type_##_destroy); \
188 #define DECLARE_TYPED_GETTER_FOR_TYPE(type_, field_, rtype_) \
189 XKB_EXPORT rtype_ type_##_get_##field_(struct type_ *object) { \
190 return object->field_; \
193 #define DECLARE_GETTER_FOR_TYPE(type_, field_) \
194 DECLARE_TYPED_GETTER_FOR_TYPE(type_, field_, const char*)
196 #define DECLARE_FIRST_NEXT_FOR_TYPE(type_, parent_type_, parent_field_) \
197 XKB_EXPORT struct type_ * type_##_first(struct parent_type_ *parent) { \
198 struct type_ *o = NULL; \
199 if (!list_empty(&parent->parent_field_)) \
200 o = list_first_entry(&parent->parent_field_, o, base.link); \
203 XKB_EXPORT struct type_ * \
204 type_##_next(struct type_ *o) \
206 struct parent_type_ *parent; \
207 struct type_ *next; \
208 parent = container_of(o->base.parent, struct parent_type_, base); \
209 next = list_first_entry(&o->base.link, o, base.link); \
210 if (list_is_last(&parent->parent_field_, &o->base.link)) \
216 rxkb_object_init(struct rxkb_object *object, struct rxkb_object *parent, destroy_func_t destroy)
218 object->refcount = 1;
219 object->destroy = destroy;
220 object->parent = parent;
221 list_init(&object->link);
225 rxkb_object_destroy(struct rxkb_object *object)
228 object->destroy(object);
229 list_remove(&object->link);
234 rxkb_object_ref(struct rxkb_object *object)
236 assert(object->refcount >= 1);
242 rxkb_object_unref(struct rxkb_object *object)
244 assert(object->refcount >= 1);
245 if (--object->refcount == 0)
246 rxkb_object_destroy(object);
251 rxkb_iso639_code_destroy(struct rxkb_iso639_code *code)
256 XKB_EXPORT struct rxkb_iso639_code *
257 rxkb_layout_get_iso639_first(struct rxkb_layout *layout)
259 struct rxkb_iso639_code *code = NULL;
261 if (!list_empty(&layout->iso639s))
262 code = list_first_entry(&layout->iso639s, code, base.link);
267 XKB_EXPORT struct rxkb_iso639_code *
268 rxkb_iso639_code_next(struct rxkb_iso639_code *code)
270 struct rxkb_iso639_code *next = NULL;
271 struct rxkb_layout *layout;
273 layout = container_of(code->base.parent, struct rxkb_layout, base);
275 if (list_is_last(&layout->iso639s, &code->base.link))
278 next = list_first_entry(&code->base.link, code, base.link);
283 DECLARE_REF_UNREF_FOR_TYPE(rxkb_iso639_code);
284 DECLARE_CREATE_FOR_TYPE(rxkb_iso639_code);
285 DECLARE_GETTER_FOR_TYPE(rxkb_iso639_code, code);
288 rxkb_iso3166_code_destroy(struct rxkb_iso3166_code *code)
293 XKB_EXPORT struct rxkb_iso3166_code *
294 rxkb_layout_get_iso3166_first(struct rxkb_layout *layout)
296 struct rxkb_iso3166_code *code = NULL;
298 if (!list_empty(&layout->iso3166s))
299 code = list_first_entry(&layout->iso3166s, code, base.link);
304 XKB_EXPORT struct rxkb_iso3166_code *
305 rxkb_iso3166_code_next(struct rxkb_iso3166_code *code)
307 struct rxkb_iso3166_code *next = NULL;
308 struct rxkb_layout *layout;
310 layout = container_of(code->base.parent, struct rxkb_layout, base);
312 if (list_is_last(&layout->iso3166s, &code->base.link))
315 next = list_first_entry(&code->base.link, code, base.link);
320 DECLARE_REF_UNREF_FOR_TYPE(rxkb_iso3166_code);
321 DECLARE_CREATE_FOR_TYPE(rxkb_iso3166_code);
322 DECLARE_GETTER_FOR_TYPE(rxkb_iso3166_code, code);
325 rxkb_option_destroy(struct rxkb_option *o)
329 free(o->description);
332 DECLARE_REF_UNREF_FOR_TYPE(rxkb_option);
333 DECLARE_CREATE_FOR_TYPE(rxkb_option);
334 DECLARE_GETTER_FOR_TYPE(rxkb_option, name);
335 DECLARE_GETTER_FOR_TYPE(rxkb_option, brief);
336 DECLARE_GETTER_FOR_TYPE(rxkb_option, description);
337 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_option, popularity, enum rxkb_popularity);
338 DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_option, rxkb_option_group, options);
341 rxkb_layout_destroy(struct rxkb_layout *l)
343 struct rxkb_iso639_code *iso639, *tmp_639;
344 struct rxkb_iso3166_code *iso3166, *tmp_3166;
348 free(l->description);
351 list_for_each_safe(iso639, tmp_639, &l->iso639s, base.link) {
352 rxkb_iso639_code_unref(iso639);
354 list_for_each_safe(iso3166, tmp_3166, &l->iso3166s, base.link) {
355 rxkb_iso3166_code_unref(iso3166);
359 DECLARE_REF_UNREF_FOR_TYPE(rxkb_layout);
360 DECLARE_CREATE_FOR_TYPE(rxkb_layout);
361 DECLARE_GETTER_FOR_TYPE(rxkb_layout, name);
362 DECLARE_GETTER_FOR_TYPE(rxkb_layout, brief);
363 DECLARE_GETTER_FOR_TYPE(rxkb_layout, description);
364 DECLARE_GETTER_FOR_TYPE(rxkb_layout, variant);
365 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_layout, popularity, enum rxkb_popularity);
366 DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_layout, rxkb_context, layouts);
369 rxkb_model_destroy(struct rxkb_model *m)
373 free(m->description);
376 DECLARE_REF_UNREF_FOR_TYPE(rxkb_model);
377 DECLARE_CREATE_FOR_TYPE(rxkb_model);
378 DECLARE_GETTER_FOR_TYPE(rxkb_model, name);
379 DECLARE_GETTER_FOR_TYPE(rxkb_model, vendor);
380 DECLARE_GETTER_FOR_TYPE(rxkb_model, description);
381 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_model, popularity, enum rxkb_popularity);
382 DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_model, rxkb_context, models);
385 rxkb_option_group_destroy(struct rxkb_option_group *og)
387 struct rxkb_option *o, *otmp;
390 free(og->description);
392 list_for_each_safe(o, otmp, &og->options, base.link) {
393 rxkb_option_unref(o);
398 rxkb_option_group_allows_multiple(struct rxkb_option_group *g)
400 return g->allow_multiple;
403 DECLARE_REF_UNREF_FOR_TYPE(rxkb_option_group);
404 DECLARE_CREATE_FOR_TYPE(rxkb_option_group);
405 DECLARE_GETTER_FOR_TYPE(rxkb_option_group, name);
406 DECLARE_GETTER_FOR_TYPE(rxkb_option_group, description);
407 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_option_group, popularity, enum rxkb_popularity);
408 DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_option_group, rxkb_context, option_groups);
411 rxkb_context_destroy(struct rxkb_context *ctx)
413 struct rxkb_model *m, *mtmp;
414 struct rxkb_layout *l, *ltmp;
415 struct rxkb_option_group *og, *ogtmp;
418 list_for_each_safe(m, mtmp, &ctx->models, base.link)
420 assert(list_empty(&ctx->models));
422 list_for_each_safe(l, ltmp, &ctx->layouts, base.link)
423 rxkb_layout_unref(l);
424 assert(list_empty(&ctx->layouts));
426 list_for_each_safe(og, ogtmp, &ctx->option_groups, base.link)
427 rxkb_option_group_unref(og);
428 assert(list_empty(&ctx->option_groups));
430 darray_foreach(path, ctx->includes)
432 darray_free(ctx->includes);
434 assert(darray_empty(ctx->includes));
437 DECLARE_REF_UNREF_FOR_TYPE(rxkb_context);
438 DECLARE_CREATE_FOR_TYPE(rxkb_context);
439 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_context, log_level, enum rxkb_log_level);
442 rxkb_context_getenv(struct rxkb_context *ctx, const char *name)
444 if (ctx->use_secure_getenv) {
445 return secure_getenv(name);
453 rxkb_context_set_log_level(struct rxkb_context *ctx,
454 enum rxkb_log_level level)
456 ctx->log_level = level;
460 log_level_to_prefix(enum rxkb_log_level level)
463 case RXKB_LOG_LEVEL_DEBUG:
464 return "xkbregistry: DEBUG: ";
465 case RXKB_LOG_LEVEL_INFO:
466 return "xkbregistry: INFO: ";
467 case RXKB_LOG_LEVEL_WARNING:
468 return "xkbregistry: WARNING: ";
469 case RXKB_LOG_LEVEL_ERROR:
470 return "xkbregistry: ERROR: ";
471 case RXKB_LOG_LEVEL_CRITICAL:
472 return "xkbregistry: CRITICAL: ";
478 ATTR_PRINTF(3, 0) static void
479 default_log_fn(struct rxkb_context *ctx, enum rxkb_log_level level,
480 const char *fmt, va_list args)
482 const char *prefix = log_level_to_prefix(level);
485 fprintf(stderr, "%s", prefix);
486 vfprintf(stderr, fmt, args);
489 static enum rxkb_log_level
490 log_level(const char *level) {
492 enum rxkb_log_level lvl;
495 lvl = strtol(level, &endptr, 10);
496 if (errno == 0 && (endptr[0] == '\0' || is_space(endptr[0])))
498 if (istreq_prefix("crit", level))
499 return RXKB_LOG_LEVEL_CRITICAL;
500 if (istreq_prefix("err", level))
501 return RXKB_LOG_LEVEL_ERROR;
502 if (istreq_prefix("warn", level))
503 return RXKB_LOG_LEVEL_WARNING;
504 if (istreq_prefix("info", level))
505 return RXKB_LOG_LEVEL_INFO;
506 if (istreq_prefix("debug", level) || istreq_prefix("dbg", level))
507 return RXKB_LOG_LEVEL_DEBUG;
509 return RXKB_LOG_LEVEL_ERROR;
512 XKB_EXPORT struct rxkb_context *
513 rxkb_context_new(enum rxkb_context_flags flags)
515 struct rxkb_context *ctx = rxkb_context_create(NULL);
521 ctx->context_state = CONTEXT_NEW;
522 ctx->load_extra_rules_files = flags & RXKB_CONTEXT_LOAD_EXOTIC_RULES;
523 ctx->use_secure_getenv = !(flags & RXKB_CONTEXT_NO_SECURE_GETENV);
524 ctx->log_fn = default_log_fn;
525 ctx->log_level = RXKB_LOG_LEVEL_ERROR;
527 /* Environment overwrites defaults. */
528 env = rxkb_context_getenv(ctx, "RXKB_LOG_LEVEL");
530 rxkb_context_set_log_level(ctx, log_level(env));
532 list_init(&ctx->models);
533 list_init(&ctx->layouts);
534 list_init(&ctx->option_groups);
536 if (!(flags & RXKB_CONTEXT_NO_DEFAULT_INCLUDES) &&
537 !rxkb_context_include_path_append_default(ctx)) {
538 rxkb_context_unref(ctx);
546 rxkb_context_set_log_fn(struct rxkb_context *ctx,
547 void (*log_fn)(struct rxkb_context *ctx,
548 enum rxkb_log_level level,
549 const char *fmt, va_list args))
551 ctx->log_fn = (log_fn ? log_fn : default_log_fn);
555 rxkb_context_include_path_append(struct rxkb_context *ctx, const char *path)
557 struct stat stat_buf;
560 char rules[PATH_MAX];
562 if (ctx->context_state != CONTEXT_NEW) {
563 log_err(ctx, "include paths can only be appended to a new context\n");
567 err = stat(path, &stat_buf);
570 if (!S_ISDIR(stat_buf.st_mode))
573 if (!check_eaccess(path, R_OK | X_OK))
576 /* Pre-filter for the 99.9% case - if we can't assemble the default ruleset
577 * path, complain here instead of during parsing later. The niche cases
578 * where this is the wrong behaviour aren't worth worrying about.
580 if (!snprintf_safe(rules, sizeof(rules), "%s/rules/%s.xml",
581 path, DEFAULT_XKB_RULES))
588 darray_append(ctx->includes, tmp);
594 rxkb_context_include_path_append_default(struct rxkb_context *ctx)
596 const char *home, *xdg, *root, *extra;
597 char user_path[PATH_MAX];
600 if (ctx->context_state != CONTEXT_NEW) {
601 log_err(ctx, "include paths can only be appended to a new context\n");
605 home = rxkb_context_getenv(ctx, "HOME");
607 xdg = rxkb_context_getenv(ctx, "XDG_CONFIG_HOME");
609 if (snprintf_safe(user_path, sizeof(user_path), "%s/xkb", xdg))
610 ret |= rxkb_context_include_path_append(ctx, user_path);
611 } else if (home != NULL) {
612 /* XDG_CONFIG_HOME fallback is $HOME/.config/ */
613 if (snprintf_safe(user_path, sizeof(user_path), "%s/.config/xkb", home))
614 ret |= rxkb_context_include_path_append(ctx, user_path);
618 if (snprintf_safe(user_path, sizeof(user_path), "%s/.xkb", home))
619 ret |= rxkb_context_include_path_append(ctx, user_path);
622 extra = rxkb_context_getenv(ctx, "XKB_CONFIG_EXTRA_PATH");
624 ret |= rxkb_context_include_path_append(ctx, extra);
626 ret |= rxkb_context_include_path_append(ctx, DFLT_XKB_CONFIG_EXTRA_PATH);
628 root = rxkb_context_getenv(ctx, "XKB_CONFIG_ROOT");
630 ret |= rxkb_context_include_path_append(ctx, root);
632 ret |= rxkb_context_include_path_append(ctx, DFLT_XKB_CONFIG_ROOT);
638 rxkb_context_parse_default_ruleset(struct rxkb_context *ctx)
640 return rxkb_context_parse(ctx, DEFAULT_XKB_RULES);
644 rxkb_context_parse(struct rxkb_context *ctx, const char *ruleset)
647 bool success = false;
649 if (ctx->context_state != CONTEXT_NEW) {
650 log_err(ctx, "parse must only be called on a new context\n");
654 darray_foreach_reverse(path, ctx->includes) {
655 char rules[PATH_MAX];
657 if (snprintf_safe(rules, sizeof(rules), "%s/rules/%s.xml",
659 log_dbg(ctx, "Parsing %s\n", rules);
660 if (parse(ctx, rules, RXKB_POPULARITY_STANDARD))
664 if (ctx->load_extra_rules_files &&
665 snprintf_safe(rules, sizeof(rules), "%s/rules/%s.extras.xml",
667 log_dbg(ctx, "Parsing %s\n", rules);
668 if (parse(ctx, rules, RXKB_POPULARITY_EXOTIC))
673 ctx->context_state = success ? CONTEXT_PARSED : CONTEXT_FAILED;
680 rxkb_context_set_user_data(struct rxkb_context *ctx, void *userdata)
682 ctx->userdata = userdata;
686 rxkb_context_get_user_data(struct rxkb_context *ctx)
688 return ctx->userdata;
692 is_node(xmlNode *node, const char *name)
694 return node->type == XML_ELEMENT_NODE &&
695 xmlStrEqual(node->name, (const xmlChar*)name);
698 /* return a copy of the text content from the first text node of this node */
700 extract_text(xmlNode *node)
704 for (n = node->children; n; n = n->next) {
705 if (n->type == XML_TEXT_NODE)
706 return (char *)xmlStrdup(n->content);
712 parse_config_item(struct rxkb_context *ctx,
719 xmlNode *node = NULL;
722 for (ci = parent->children; ci; ci = ci->next) {
723 if (is_node(ci, "configItem")) {
729 for (node = ci->children; node; node = node->next) {
730 if (is_node(node, "name"))
731 *name = extract_text(node);
732 else if (is_node(node, "description"))
733 *description = extract_text(node);
734 else if (is_node(node, "shortDescription"))
735 *brief = extract_text(node);
736 else if (is_node(node, "vendor"))
737 *vendor = extract_text(node);
738 /* Note: the DTD allows for vendor + brief but models only use
739 * vendor and everything else only uses shortDescription */
742 if (!*name || !strlen(*name)) {
743 log_err(ctx, "xml:%d: missing required element 'name'\n",
752 return true; /* only one configItem allowed in the dtd */
760 parse_model(struct rxkb_context *ctx, xmlNode *model,
761 enum rxkb_popularity popularity)
763 char *name, *description, *brief, *vendor;
765 if (parse_config_item(ctx, model, &name, &description, &brief, &vendor)) {
766 struct rxkb_model *m;
768 list_for_each(m, &ctx->models, base.link) {
769 if (streq(m->name, name)) {
779 m = rxkb_model_create(&ctx->base);
781 m->description = description;
783 m->popularity = popularity;
784 list_append(&ctx->models, &m->base.link);
789 parse_model_list(struct rxkb_context *ctx, xmlNode *model_list,
790 enum rxkb_popularity popularity)
792 xmlNode *node = NULL;
794 for (node = model_list->children; node; node = node->next) {
795 if (is_node(node, "model"))
796 parse_model(ctx, node, popularity);
801 parse_language_list(xmlNode *language_list, struct rxkb_layout *layout)
803 xmlNode *node = NULL;
804 struct rxkb_iso639_code *code;
806 for (node = language_list->children; node; node = node->next) {
807 if (is_node(node, "iso639Id")) {
808 char *str = extract_text(node);
809 struct rxkb_object *parent;
811 if (!str || strlen(str) != 3) {
816 parent = &layout->base;
817 code = rxkb_iso639_code_create(parent);
819 list_append(&layout->iso639s, &code->base.link);
825 parse_country_list(xmlNode *country_list, struct rxkb_layout *layout)
827 xmlNode *node = NULL;
828 struct rxkb_iso3166_code *code;
830 for (node = country_list->children; node; node = node->next) {
831 if (is_node(node, "iso3166Id")) {
832 char *str = extract_text(node);
833 struct rxkb_object *parent;
835 if (!str || strlen(str) != 2) {
840 parent = &layout->base;
841 code = rxkb_iso3166_code_create(parent);
843 list_append(&layout->iso3166s, &code->base.link);
849 parse_variant(struct rxkb_context *ctx, struct rxkb_layout *l,
850 xmlNode *variant, enum rxkb_popularity popularity)
853 char *name, *description, *brief, *vendor;
855 if (parse_config_item(ctx, variant, &name, &description, &brief, &vendor)) {
856 struct rxkb_layout *v;
859 list_for_each(v, &ctx->layouts, base.link) {
860 if (streq(v->name, name) && streq(v->name, l->name)) {
867 v = rxkb_layout_create(&ctx->base);
868 list_init(&v->iso639s);
869 list_init(&v->iso3166s);
870 v->name = strdup(l->name);
872 v->description = description;
873 // if variant omits brief, inherit from parent layout.
874 v->brief = brief == NULL ? strdup_safe(l->brief) : brief;
875 v->popularity = popularity;
876 list_append(&ctx->layouts, &v->base.link);
878 for (ci = variant->children; ci; ci = ci->next) {
881 if (!is_node(ci, "configItem"))
884 bool found_language_list = false;
885 bool found_country_list = false;
886 for (node = ci->children; node; node = node->next) {
887 if (is_node(node, "languageList")) {
888 parse_language_list(node, v);
889 found_language_list = true;
891 if (is_node(node, "countryList")) {
892 parse_country_list(node, v);
893 found_country_list = true;
896 if (!found_language_list) {
897 // inherit from parent layout
898 struct rxkb_iso639_code* x;
899 list_for_each(x, &l->iso639s, base.link) {
900 struct rxkb_iso639_code* code = rxkb_iso639_code_create(&v->base);
901 code->code = strdup(x->code);
902 list_append(&v->iso639s, &code->base.link);
905 if (!found_country_list) {
906 // inherit from parent layout
907 struct rxkb_iso3166_code* x;
908 list_for_each(x, &l->iso3166s, base.link) {
909 struct rxkb_iso3166_code* code = rxkb_iso3166_code_create(&v->base);
910 code->code = strdup(x->code);
911 list_append(&v->iso3166s, &code->base.link);
925 parse_variant_list(struct rxkb_context *ctx, struct rxkb_layout *l,
926 xmlNode *variant_list, enum rxkb_popularity popularity)
928 xmlNode *node = NULL;
930 for (node = variant_list->children; node; node = node->next) {
931 if (is_node(node, "variant"))
932 parse_variant(ctx, l, node, popularity);
937 parse_layout(struct rxkb_context *ctx, xmlNode *layout,
938 enum rxkb_popularity popularity)
940 char *name, *description, *brief, *vendor;
941 struct rxkb_layout *l;
942 xmlNode *node = NULL;
945 if (!parse_config_item(ctx, layout, &name, &description, &brief, &vendor))
948 list_for_each(l, &ctx->layouts, base.link) {
949 if (streq(l->name, name) && l->variant == NULL) {
956 l = rxkb_layout_create(&ctx->base);
957 list_init(&l->iso639s);
958 list_init(&l->iso3166s);
961 l->description = description;
963 l->popularity = popularity;
964 list_append(&ctx->layouts, &l->base.link);
972 for (node = layout->children; node; node = node->next) {
973 if (is_node(node, "variantList")) {
974 parse_variant_list(ctx, l, node, popularity);
976 if (!exists && is_node(node, "configItem")) {
978 for (ll = node->children; ll; ll = ll->next) {
979 if (is_node(ll, "languageList"))
980 parse_language_list(ll, l);
981 if (is_node(ll, "countryList"))
982 parse_country_list(ll, l);
989 parse_layout_list(struct rxkb_context *ctx, xmlNode *layout_list,
990 enum rxkb_popularity popularity)
992 xmlNode *node = NULL;
994 for (node = layout_list->children; node; node = node->next) {
995 if (is_node(node, "layout"))
996 parse_layout(ctx, node, popularity);
1001 parse_option(struct rxkb_context *ctx, struct rxkb_option_group *group,
1002 xmlNode *option, enum rxkb_popularity popularity)
1004 char *name, *description, *brief, *vendor;
1006 if (parse_config_item(ctx, option, &name, &description, &brief, &vendor)) {
1007 struct rxkb_option *o;
1009 list_for_each(o, &group->options, base.link) {
1010 if (streq(o->name, name)) {
1019 o = rxkb_option_create(&group->base);
1021 o->description = description;
1022 o->popularity = popularity;
1023 list_append(&group->options, &o->base.link);
1028 parse_group(struct rxkb_context *ctx, xmlNode *group,
1029 enum rxkb_popularity popularity)
1031 char *name, *description, *brief, *vendor;
1032 struct rxkb_option_group *g;
1033 xmlNode *node = NULL;
1035 bool exists = false;
1037 if (!parse_config_item(ctx, group, &name, &description, &brief, &vendor))
1040 list_for_each(g, &ctx->option_groups, base.link) {
1041 if (streq(g->name, name)) {
1048 g = rxkb_option_group_create(&ctx->base);
1050 g->description = description;
1051 g->popularity = popularity;
1053 multiple = xmlGetProp(group, (const xmlChar*)"allowMultipleSelection");
1054 if (multiple && xmlStrEqual(multiple, (const xmlChar*)"true"))
1055 g->allow_multiple = true;
1058 list_init(&g->options);
1059 list_append(&ctx->option_groups, &g->base.link);
1067 for (node = group->children; node; node = node->next) {
1068 if (is_node(node, "option"))
1069 parse_option(ctx, g, node, popularity);
1074 parse_option_list(struct rxkb_context *ctx, xmlNode *option_list,
1075 enum rxkb_popularity popularity)
1077 xmlNode *node = NULL;
1079 for (node = option_list->children; node; node = node->next) {
1080 if (is_node(node, "group"))
1081 parse_group(ctx, node, popularity);
1086 parse_rules_xml(struct rxkb_context *ctx, xmlNode *root,
1087 enum rxkb_popularity popularity)
1089 xmlNode *node = NULL;
1091 for (node = root->children; node; node = node->next) {
1092 if (is_node(node, "modelList"))
1093 parse_model_list(ctx, node, popularity);
1094 else if (is_node(node, "layoutList"))
1095 parse_layout_list(ctx, node, popularity);
1096 else if (is_node(node, "optionList"))
1097 parse_option_list(ctx, node, popularity);
1103 xml_error_func(void *ctx, const char *msg, ...)
1105 static char buf[PATH_MAX];
1106 static int slen = 0;
1110 /* libxml2 prints IO errors from bad includes paths by
1111 * calling the error function once per word. So we get to
1112 * re-assemble the message here and print it when we get
1113 * the line break. My enthusiasm about this is indescribable.
1115 va_start(args, msg);
1116 rc = vsnprintf(&buf[slen], sizeof(buf) - slen, msg, args);
1119 /* This shouldn't really happen */
1121 log_err(ctx, "+++ out of cheese error. redo from start +++\n");
1123 memset(buf, 0, sizeof(buf));
1128 if (slen >= (int)sizeof(buf)) {
1129 /* truncated, let's flush this */
1130 buf[sizeof(buf) - 1] = '\n';
1134 /* We're assuming here that the last character is \n. */
1135 if (buf[slen - 1] == '\n') {
1136 log_err(ctx, "%s", buf);
1137 memset(buf, 0, sizeof(buf));
1143 validate(struct rxkb_context *ctx, xmlDoc *doc)
1145 bool success = false;
1146 xmlValidCtxt *dtdvalid = NULL;
1148 xmlParserInputBufferPtr buf = NULL;
1149 /* This is a modified version of the xkeyboard-config xkb.dtd. That one
1150 * requires modelList, layoutList and optionList, we
1151 * allow for any of those to be missing.
1153 const char dtdstr[] =
1154 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1155 "<!ELEMENT xkbConfigRegistry (modelList?, layoutList?, optionList?)>\n"
1156 "<!ATTLIST xkbConfigRegistry version CDATA \"1.1\">\n"
1157 "<!ELEMENT modelList (model*)>\n"
1158 "<!ELEMENT model (configItem)>\n"
1159 "<!ELEMENT layoutList (layout*)>\n"
1160 "<!ELEMENT layout (configItem, variantList?)>\n"
1161 "<!ELEMENT optionList (group*)>\n"
1162 "<!ELEMENT variantList (variant*)>\n"
1163 "<!ELEMENT variant (configItem)>\n"
1164 "<!ELEMENT group (configItem, option*)>\n"
1165 "<!ATTLIST group allowMultipleSelection (true|false) \"false\">\n"
1166 "<!ELEMENT option (configItem)>\n"
1167 "<!ELEMENT configItem (name, shortDescription?, description?, vendor?, countryList?, languageList?, hwList?)>\n"
1168 "<!ATTLIST configItem popularity (standard|exotic) \"standard\">\n"
1169 "<!ELEMENT name (#PCDATA)>\n"
1170 "<!ELEMENT shortDescription (#PCDATA)>\n"
1171 "<!ELEMENT description (#PCDATA)>\n"
1172 "<!ELEMENT vendor (#PCDATA)>\n"
1173 "<!ELEMENT countryList (iso3166Id+)>\n"
1174 "<!ELEMENT iso3166Id (#PCDATA)>\n"
1175 "<!ELEMENT languageList (iso639Id+)>\n"
1176 "<!ELEMENT iso639Id (#PCDATA)>\n"
1177 "<!ELEMENT hwList (hwId+)>\n"
1178 "<!ELEMENT hwId (#PCDATA)>\n";
1180 /* Note: do not use xmlParserInputBufferCreateStatic, it generates random
1181 * DTD validity errors for unknown reasons */
1182 buf = xmlParserInputBufferCreateMem(dtdstr, sizeof(dtdstr),
1183 XML_CHAR_ENCODING_UTF8);
1187 dtd = xmlIOParseDTD(NULL, buf, XML_CHAR_ENCODING_UTF8);
1189 log_err(ctx, "Failed to load DTD\n");
1193 dtdvalid = xmlNewValidCtxt();
1194 if (xmlValidateDtd(dtdvalid, doc, dtd))
1200 xmlFreeValidCtxt(dtdvalid);
1206 parse(struct rxkb_context *ctx, const char *path,
1207 enum rxkb_popularity popularity)
1209 bool success = false;
1211 xmlNode *root = NULL;
1213 if (!check_eaccess(path, R_OK))
1218 xmlSetGenericErrorFunc(ctx, xml_error_func);
1220 doc = xmlParseFile(path);
1224 if (!validate(ctx, doc)) {
1225 log_err(ctx, "XML error: failed to validate document at %s\n", path);
1229 root = xmlDocGetRootElement(doc);
1230 parse_rules_xml(ctx, root, popularity);