1 /*
2 * Copyright © 2020 Red Hat, Inc.
3 *
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:
10 *
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
13 * Software.
14 *
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.
22 */
23
24 #include "config.h"
25
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <stdbool.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdint.h>
32 #include <libxml/parser.h>
33
34 #include "xkbcommon/xkbregistry.h"
35 #include "utils.h"
36 #include "util-list.h"
37
38 struct rxkb_object;
39
40 typedef void (*destroy_func_t)(struct rxkb_object *object);
41
42 /**
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.
46 */
47 struct rxkb_object {
48 struct rxkb_object *parent;
49 uint32_t refcount;
50 struct list link;
51 destroy_func_t destroy;
52 };
53
54 struct rxkb_iso639_code {
55 struct rxkb_object base;
56 char *code;
57 };
58
59 struct rxkb_iso3166_code {
60 struct rxkb_object base;
61 char *code;
62 };
63
64 enum context_state {
65 CONTEXT_NEW,
66 CONTEXT_PARSED,
67 CONTEXT_FAILED,
68 };
69
70 struct rxkb_context {
71 struct rxkb_object base;
72 enum context_state context_state;
73
74 bool load_extra_rules_files;
75
76 struct list models; /* list of struct rxkb_models */
77 struct list layouts; /* list of struct rxkb_layouts */
78 struct list option_groups; /* list of struct rxkb_option_group */
79
80 darray(char *) includes;
81
82
83 ATTR_PRINTF(3, 0) void (*log_fn)(struct rxkb_context *ctx,
84 enum rxkb_log_level level,
85 const char *fmt, va_list args);
86 enum rxkb_log_level log_level;
87
88 void *userdata;
89 };
90
91 struct rxkb_model {
92 struct rxkb_object base;
93
94 char *name;
95 char *vendor;
96 char *description;
97 enum rxkb_popularity popularity;
98 };
99
100 struct rxkb_layout {
101 struct rxkb_object base;
102
103 char *name;
104 char *brief;
105 char *description;
106 char *variant;
107 enum rxkb_popularity popularity;
108
109 struct list iso639s; /* list of struct rxkb_iso639_code */
110 struct list iso3166s; /* list of struct rxkb_iso3166_code */
111 };
112
113 struct rxkb_option_group {
114 struct rxkb_object base;
115
116 bool allow_multiple;
117 struct list options; /* list of struct rxkb_options */
118 char *name;
119 char *description;
120 enum rxkb_popularity popularity;
121 };
122
123 struct rxkb_option {
124 struct rxkb_object base;
125
126 char *name;
127 char *brief;
128 char *description;
129 enum rxkb_popularity popularity;
130 };
131
132 static bool
133 parse(struct rxkb_context *ctx, const char *path,
134 enum rxkb_popularity popularity);
135
136 ATTR_PRINTF(3, 4)
137 static void
rxkb_log(struct rxkb_context * ctx,enum rxkb_log_level level,const char * fmt,...)138 rxkb_log(struct rxkb_context *ctx, enum rxkb_log_level level,
139 const char *fmt, ...)
140 {
141 va_list args;
142
143 if (ctx->log_level < level)
144 return;
145
146 va_start(args, fmt);
147 ctx->log_fn(ctx, level, fmt, args);
148 va_end(args);
149 }
150
151 /*
152 * The format is not part of the argument list in order to avoid the
153 * "ISO C99 requires rest arguments to be used" warning when only the
154 * format is supplied without arguments. Not supplying it would still
155 * result in an error, though.
156 */
157 #define log_dbg(ctx, ...) \
158 rxkb_log((ctx), RXKB_LOG_LEVEL_DEBUG, __VA_ARGS__)
159 #define log_info(ctx, ...) \
160 rxkb_log((ctx), RXKB_LOG_LEVEL_INFO, __VA_ARGS__)
161 #define log_warn(ctx, ...) \
162 rxkb_log((ctx), RXKB_LOG_LEVEL_WARNING, __VA_ARGS__)
163 #define log_err(ctx, ...) \
164 rxkb_log((ctx), RXKB_LOG_LEVEL_ERROR, __VA_ARGS__)
165 #define log_wsgo(ctx, ...) \
166 rxkb_log((ctx), RXKB_LOG_LEVEL_CRITICAL, __VA_ARGS__)
167
168
169 #define DECLARE_REF_UNREF_FOR_TYPE(type_) \
170 XKB_EXPORT struct type_ * type_##_ref(struct type_ *object) { \
171 rxkb_object_ref(&object->base); \
172 return object; \
173 } \
174 XKB_EXPORT struct type_ * type_##_unref(struct type_ *object) { \
175 if (!object) return NULL; \
176 return rxkb_object_unref(&object->base); \
177 }
178
179 #define DECLARE_CREATE_FOR_TYPE(type_) \
180 static inline struct type_ * type_##_create(struct rxkb_object *parent) { \
181 struct type_ *t = calloc(1, sizeof *t); \
182 if (t) \
183 rxkb_object_init(&t->base, parent, (destroy_func_t)type_##_destroy); \
184 return t; \
185 }
186
187 #define DECLARE_TYPED_GETTER_FOR_TYPE(type_, field_, rtype_) \
188 XKB_EXPORT rtype_ type_##_get_##field_(struct type_ *object) { \
189 return object->field_; \
190 }
191
192 #define DECLARE_GETTER_FOR_TYPE(type_, field_) \
193 DECLARE_TYPED_GETTER_FOR_TYPE(type_, field_, const char*)
194
195 #define DECLARE_FIRST_NEXT_FOR_TYPE(type_, parent_type_, parent_field_) \
196 XKB_EXPORT struct type_ * type_##_first(struct parent_type_ *parent) { \
197 struct type_ *o = NULL; \
198 if (!list_empty(&parent->parent_field_)) \
199 o = list_first_entry(&parent->parent_field_, o, base.link); \
200 return o; \
201 } \
202 XKB_EXPORT struct type_ * \
203 type_##_next(struct type_ *o) \
204 { \
205 struct parent_type_ *parent; \
206 struct type_ *next; \
207 parent = container_of(o->base.parent, struct parent_type_, base); \
208 next = list_first_entry(&o->base.link, o, base.link); \
209 if (list_is_last(&parent->parent_field_, &o->base.link)) \
210 return NULL; \
211 return next; \
212 }
213
214 static void
rxkb_object_init(struct rxkb_object * object,struct rxkb_object * parent,destroy_func_t destroy)215 rxkb_object_init(struct rxkb_object *object, struct rxkb_object *parent, destroy_func_t destroy)
216 {
217 object->refcount = 1;
218 object->destroy = destroy;
219 object->parent = parent;
220 list_init(&object->link);
221 }
222
223 static void
rxkb_object_destroy(struct rxkb_object * object)224 rxkb_object_destroy(struct rxkb_object *object)
225 {
226 if (object->destroy)
227 object->destroy(object);
228 list_remove(&object->link);
229 free(object);
230 }
231
232 static void *
rxkb_object_ref(struct rxkb_object * object)233 rxkb_object_ref(struct rxkb_object *object)
234 {
235 assert(object->refcount >= 1);
236 ++object->refcount;
237 return object;
238 }
239
240 static void *
rxkb_object_unref(struct rxkb_object * object)241 rxkb_object_unref(struct rxkb_object *object)
242 {
243 assert(object->refcount >= 1);
244 if (--object->refcount == 0)
245 rxkb_object_destroy(object);
246 return NULL;
247 }
248
249 static void
rxkb_iso639_code_destroy(struct rxkb_iso639_code * code)250 rxkb_iso639_code_destroy(struct rxkb_iso639_code *code)
251 {
252 free(code->code);
253 }
254
255 XKB_EXPORT struct rxkb_iso639_code *
rxkb_layout_get_iso639_first(struct rxkb_layout * layout)256 rxkb_layout_get_iso639_first(struct rxkb_layout *layout)
257 {
258 struct rxkb_iso639_code *code = NULL;
259
260 if (!list_empty(&layout->iso639s))
261 code = list_first_entry(&layout->iso639s, code, base.link);
262
263 return code;
264 }
265
266 XKB_EXPORT struct rxkb_iso639_code *
rxkb_iso639_code_next(struct rxkb_iso639_code * code)267 rxkb_iso639_code_next(struct rxkb_iso639_code *code)
268 {
269 struct rxkb_iso639_code *next = NULL;
270 struct rxkb_layout *layout;
271
272 layout = container_of(code->base.parent, struct rxkb_layout, base);
273
274 if (list_is_last(&layout->iso639s, &code->base.link))
275 return NULL;
276
277 next = list_first_entry(&code->base.link, code, base.link);
278
279 return next;
280 }
281
282 DECLARE_REF_UNREF_FOR_TYPE(rxkb_iso639_code);
283 DECLARE_CREATE_FOR_TYPE(rxkb_iso639_code);
284 DECLARE_GETTER_FOR_TYPE(rxkb_iso639_code, code);
285
286 static void
rxkb_iso3166_code_destroy(struct rxkb_iso3166_code * code)287 rxkb_iso3166_code_destroy(struct rxkb_iso3166_code *code)
288 {
289 free(code->code);
290 }
291
292 XKB_EXPORT struct rxkb_iso3166_code *
rxkb_layout_get_iso3166_first(struct rxkb_layout * layout)293 rxkb_layout_get_iso3166_first(struct rxkb_layout *layout)
294 {
295 struct rxkb_iso3166_code *code = NULL;
296
297 if (!list_empty(&layout->iso3166s))
298 code = list_first_entry(&layout->iso3166s, code, base.link);
299
300 return code;
301 }
302
303 XKB_EXPORT struct rxkb_iso3166_code *
rxkb_iso3166_code_next(struct rxkb_iso3166_code * code)304 rxkb_iso3166_code_next(struct rxkb_iso3166_code *code)
305 {
306 struct rxkb_iso3166_code *next = NULL;
307 struct rxkb_layout *layout;
308
309 layout = container_of(code->base.parent, struct rxkb_layout, base);
310
311 if (list_is_last(&layout->iso3166s, &code->base.link))
312 return NULL;
313
314 next = list_first_entry(&code->base.link, code, base.link);
315
316 return next;
317 }
318
319 DECLARE_REF_UNREF_FOR_TYPE(rxkb_iso3166_code);
320 DECLARE_CREATE_FOR_TYPE(rxkb_iso3166_code);
321 DECLARE_GETTER_FOR_TYPE(rxkb_iso3166_code, code);
322
323 static void
rxkb_option_destroy(struct rxkb_option * o)324 rxkb_option_destroy(struct rxkb_option *o)
325 {
326 free(o->name);
327 free(o->brief);
328 free(o->description);
329 }
330
331 DECLARE_REF_UNREF_FOR_TYPE(rxkb_option);
332 DECLARE_CREATE_FOR_TYPE(rxkb_option);
333 DECLARE_GETTER_FOR_TYPE(rxkb_option, name);
334 DECLARE_GETTER_FOR_TYPE(rxkb_option, brief);
335 DECLARE_GETTER_FOR_TYPE(rxkb_option, description);
336 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_option, popularity, enum rxkb_popularity);
337 DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_option, rxkb_option_group, options);
338
339 static void
rxkb_layout_destroy(struct rxkb_layout * l)340 rxkb_layout_destroy(struct rxkb_layout *l)
341 {
342 struct rxkb_iso639_code *iso639, *tmp_639;
343 struct rxkb_iso3166_code *iso3166, *tmp_3166;
344
345 free(l->name);
346 free(l->brief);
347 free(l->description);
348 free(l->variant);
349
350 list_for_each_safe(iso639, tmp_639, &l->iso639s, base.link) {
351 rxkb_iso639_code_unref(iso639);
352 }
353 list_for_each_safe(iso3166, tmp_3166, &l->iso3166s, base.link) {
354 rxkb_iso3166_code_unref(iso3166);
355 }
356 }
357
358 DECLARE_REF_UNREF_FOR_TYPE(rxkb_layout);
359 DECLARE_CREATE_FOR_TYPE(rxkb_layout);
360 DECLARE_GETTER_FOR_TYPE(rxkb_layout, name);
361 DECLARE_GETTER_FOR_TYPE(rxkb_layout, brief);
362 DECLARE_GETTER_FOR_TYPE(rxkb_layout, description);
363 DECLARE_GETTER_FOR_TYPE(rxkb_layout, variant);
364 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_layout, popularity, enum rxkb_popularity);
365 DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_layout, rxkb_context, layouts);
366
367 static void
rxkb_model_destroy(struct rxkb_model * m)368 rxkb_model_destroy(struct rxkb_model *m)
369 {
370 free(m->name);
371 free(m->vendor);
372 free(m->description);
373 }
374
375 DECLARE_REF_UNREF_FOR_TYPE(rxkb_model);
376 DECLARE_CREATE_FOR_TYPE(rxkb_model);
377 DECLARE_GETTER_FOR_TYPE(rxkb_model, name);
378 DECLARE_GETTER_FOR_TYPE(rxkb_model, vendor);
379 DECLARE_GETTER_FOR_TYPE(rxkb_model, description);
380 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_model, popularity, enum rxkb_popularity);
381 DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_model, rxkb_context, models);
382
383 static void
rxkb_option_group_destroy(struct rxkb_option_group * og)384 rxkb_option_group_destroy(struct rxkb_option_group *og)
385 {
386 struct rxkb_option *o, *otmp;
387
388 free(og->name);
389 free(og->description);
390
391 list_for_each_safe(o, otmp, &og->options, base.link) {
392 rxkb_option_unref(o);
393 }
394 }
395
396 XKB_EXPORT bool
rxkb_option_group_allows_multiple(struct rxkb_option_group * g)397 rxkb_option_group_allows_multiple(struct rxkb_option_group *g)
398 {
399 return g->allow_multiple;
400 }
401
402 DECLARE_REF_UNREF_FOR_TYPE(rxkb_option_group);
403 DECLARE_CREATE_FOR_TYPE(rxkb_option_group);
404 DECLARE_GETTER_FOR_TYPE(rxkb_option_group, name);
405 DECLARE_GETTER_FOR_TYPE(rxkb_option_group, description);
406 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_option_group, popularity, enum rxkb_popularity);
407 DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_option_group, rxkb_context, option_groups);
408
409 static void
rxkb_context_destroy(struct rxkb_context * ctx)410 rxkb_context_destroy(struct rxkb_context *ctx)
411 {
412 struct rxkb_model *m, *mtmp;
413 struct rxkb_layout *l, *ltmp;
414 struct rxkb_option_group *og, *ogtmp;
415 char **path;
416
417 list_for_each_safe(m, mtmp, &ctx->models, base.link)
418 rxkb_model_unref(m);
419 assert(list_empty(&ctx->models));
420
421 list_for_each_safe(l, ltmp, &ctx->layouts, base.link)
422 rxkb_layout_unref(l);
423 assert(list_empty(&ctx->layouts));
424
425 list_for_each_safe(og, ogtmp, &ctx->option_groups, base.link)
426 rxkb_option_group_unref(og);
427 assert(list_empty(&ctx->option_groups));
428
429 darray_foreach(path, ctx->includes)
430 free(*path);
431 darray_free(ctx->includes);
432
433 assert(darray_empty(ctx->includes));
434 }
435
436 DECLARE_REF_UNREF_FOR_TYPE(rxkb_context);
437 DECLARE_CREATE_FOR_TYPE(rxkb_context);
438 DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_context, log_level, enum rxkb_log_level);
439
440 XKB_EXPORT void
rxkb_context_set_log_level(struct rxkb_context * ctx,enum rxkb_log_level level)441 rxkb_context_set_log_level(struct rxkb_context *ctx,
442 enum rxkb_log_level level)
443 {
444 ctx->log_level = level;
445 }
446
447 static const char *
log_level_to_prefix(enum rxkb_log_level level)448 log_level_to_prefix(enum rxkb_log_level level)
449 {
450 switch (level) {
451 case RXKB_LOG_LEVEL_DEBUG:
452 return "xkbregistry: DEBUG: ";
453 case RXKB_LOG_LEVEL_INFO:
454 return "xkbregistry: INFO: ";
455 case RXKB_LOG_LEVEL_WARNING:
456 return "xkbregistry: WARNING: ";
457 case RXKB_LOG_LEVEL_ERROR:
458 return "xkbregistry: ERROR: ";
459 case RXKB_LOG_LEVEL_CRITICAL:
460 return "xkbregistry: CRITICAL: ";
461 default:
462 return NULL;
463 }
464 }
465
466 ATTR_PRINTF(3, 0) static void
default_log_fn(struct rxkb_context * ctx,enum rxkb_log_level level,const char * fmt,va_list args)467 default_log_fn(struct rxkb_context *ctx, enum rxkb_log_level level,
468 const char *fmt, va_list args)
469 {
470 const char *prefix = log_level_to_prefix(level);
471
472 if (prefix)
473 fprintf(stderr, "%s", prefix);
474 vfprintf(stderr, fmt, args);
475 }
476
477 static enum rxkb_log_level
log_level(const char * level)478 log_level(const char *level) {
479 char *endptr;
480 enum rxkb_log_level lvl;
481
482 errno = 0;
483 lvl = strtol(level, &endptr, 10);
484 if (errno == 0 && (endptr[0] == '\0' || is_space(endptr[0])))
485 return lvl;
486 if (istreq_prefix("crit", level))
487 return RXKB_LOG_LEVEL_CRITICAL;
488 if (istreq_prefix("err", level))
489 return RXKB_LOG_LEVEL_ERROR;
490 if (istreq_prefix("warn", level))
491 return RXKB_LOG_LEVEL_WARNING;
492 if (istreq_prefix("info", level))
493 return RXKB_LOG_LEVEL_INFO;
494 if (istreq_prefix("debug", level) || istreq_prefix("dbg", level))
495 return RXKB_LOG_LEVEL_DEBUG;
496
497 return RXKB_LOG_LEVEL_ERROR;
498 }
499
500 XKB_EXPORT struct rxkb_context *
rxkb_context_new(enum rxkb_context_flags flags)501 rxkb_context_new(enum rxkb_context_flags flags)
502 {
503 struct rxkb_context *ctx = rxkb_context_create(NULL);
504 const char *env;
505
506 if (!ctx)
507 return NULL;
508
509 ctx->context_state = CONTEXT_NEW;
510 ctx->load_extra_rules_files = flags & RXKB_CONTEXT_LOAD_EXOTIC_RULES;
511 ctx->log_fn = default_log_fn;
512 ctx->log_level = RXKB_LOG_LEVEL_ERROR;
513
514 /* Environment overwrites defaults. */
515 env = secure_getenv("RXKB_LOG_LEVEL");
516 if (env)
517 rxkb_context_set_log_level(ctx, log_level(env));
518
519 list_init(&ctx->models);
520 list_init(&ctx->layouts);
521 list_init(&ctx->option_groups);
522
523 if (!(flags & RXKB_CONTEXT_NO_DEFAULT_INCLUDES) &&
524 !rxkb_context_include_path_append_default(ctx)) {
525 rxkb_context_unref(ctx);
526 return NULL;
527 }
528
529 return ctx;
530 }
531
532 XKB_EXPORT void
rxkb_context_set_log_fn(struct rxkb_context * ctx,void (* log_fn)(struct rxkb_context * ctx,enum rxkb_log_level level,const char * fmt,va_list args))533 rxkb_context_set_log_fn(struct rxkb_context *ctx,
534 void (*log_fn)(struct rxkb_context *ctx,
535 enum rxkb_log_level level,
536 const char *fmt, va_list args))
537 {
538 ctx->log_fn = (log_fn ? log_fn : default_log_fn);
539 }
540
541 XKB_EXPORT bool
rxkb_context_include_path_append(struct rxkb_context * ctx,const char * path)542 rxkb_context_include_path_append(struct rxkb_context *ctx, const char *path)
543 {
544 struct stat stat_buf;
545 int err;
546 char *tmp = NULL;
547 char rules[PATH_MAX];
548
549 if (ctx->context_state != CONTEXT_NEW) {
550 log_err(ctx, "include paths can only be appended to a new context\n");
551 return false;
552 }
553
554 tmp = strdup(path);
555 if (!tmp)
556 goto err;
557
558 err = stat(path, &stat_buf);
559 if (err != 0)
560 goto err;
561 if (!S_ISDIR(stat_buf.st_mode))
562 goto err;
563
564 if (!check_eaccess(path, R_OK | X_OK))
565 goto err;
566
567 /* Pre-filter for the 99.9% case - if we can't assemble the default ruleset
568 * path, complain here instead of during parsing later. The niche cases
569 * where this is the wrong behaviour aren't worth worrying about.
570 */
571 if (!snprintf_safe(rules, sizeof(rules), "%s/rules/%s.xml",
572 path, DEFAULT_XKB_RULES))
573 goto err;
574
575 darray_append(ctx->includes, tmp);
576
577 return true;
578
579 err:
580 free(tmp);
581 return false;
582 }
583
584 XKB_EXPORT bool
rxkb_context_include_path_append_default(struct rxkb_context * ctx)585 rxkb_context_include_path_append_default(struct rxkb_context *ctx)
586 {
587 const char *home, *xdg, *root, *extra;
588 char *user_path;
589 bool ret = false;
590
591 if (ctx->context_state != CONTEXT_NEW) {
592 log_err(ctx, "include paths can only be appended to a new context\n");
593 return false;
594 }
595
596 home = secure_getenv("HOME");
597
598 xdg = secure_getenv("XDG_CONFIG_HOME");
599 if (xdg != NULL) {
600 user_path = asprintf_safe("%s/xkb", xdg);
601 if (user_path) {
602 ret |= rxkb_context_include_path_append(ctx, user_path);
603 free(user_path);
604 }
605 } else if (home != NULL) {
606 /* XDG_CONFIG_HOME fallback is $HOME/.config/ */
607 user_path = asprintf_safe("%s/.config/xkb", home);
608 if (user_path) {
609 ret |= rxkb_context_include_path_append(ctx, user_path);
610 free(user_path);
611 }
612 }
613
614 if (home != NULL) {
615 user_path = asprintf_safe("%s/.xkb", home);
616 if (user_path) {
617 ret |= rxkb_context_include_path_append(ctx, user_path);
618 free(user_path);
619 }
620 }
621
622 extra = secure_getenv("XKB_CONFIG_EXTRA_PATH");
623 if (extra != NULL)
624 ret |= rxkb_context_include_path_append(ctx, extra);
625 else
626 ret |= rxkb_context_include_path_append(ctx, DFLT_XKB_CONFIG_EXTRA_PATH);
627
628 root = secure_getenv("XKB_CONFIG_ROOT");
629 if (root != NULL)
630 ret |= rxkb_context_include_path_append(ctx, root);
631 else
632 ret |= rxkb_context_include_path_append(ctx, DFLT_XKB_CONFIG_ROOT);
633
634 return ret;
635 }
636
637 XKB_EXPORT bool
rxkb_context_parse_default_ruleset(struct rxkb_context * ctx)638 rxkb_context_parse_default_ruleset(struct rxkb_context *ctx)
639 {
640 return rxkb_context_parse(ctx, DEFAULT_XKB_RULES);
641 }
642
643 XKB_EXPORT bool
rxkb_context_parse(struct rxkb_context * ctx,const char * ruleset)644 rxkb_context_parse(struct rxkb_context *ctx, const char *ruleset)
645 {
646 char **path;
647 bool success = false;
648
649 if (ctx->context_state != CONTEXT_NEW) {
650 log_err(ctx, "parse must only be called on a new context\n");
651 return false;
652 }
653
654 darray_foreach_reverse(path, ctx->includes) {
655 char rules[PATH_MAX];
656
657 if (snprintf_safe(rules, sizeof(rules), "%s/rules/%s.xml",
658 *path, ruleset)) {
659 log_dbg(ctx, "Parsing %s\n", rules);
660 if (parse(ctx, rules, RXKB_POPULARITY_STANDARD))
661 success = true;
662 }
663
664 if (ctx->load_extra_rules_files &&
665 snprintf_safe(rules, sizeof(rules), "%s/rules/%s.extras.xml",
666 *path, ruleset)) {
667 log_dbg(ctx, "Parsing %s\n", rules);
668 if (parse(ctx, rules, RXKB_POPULARITY_EXOTIC))
669 success = true;
670 }
671 }
672
673 ctx->context_state = success ? CONTEXT_PARSED : CONTEXT_FAILED;
674
675 return success;
676 }
677
678
679 XKB_EXPORT void
rxkb_context_set_user_data(struct rxkb_context * ctx,void * userdata)680 rxkb_context_set_user_data(struct rxkb_context *ctx, void *userdata)
681 {
682 ctx->userdata = userdata;
683 }
684
685 XKB_EXPORT void *
rxkb_context_get_user_data(struct rxkb_context * ctx)686 rxkb_context_get_user_data(struct rxkb_context *ctx)
687 {
688 return ctx->userdata;
689 }
690
691 static inline bool
is_node(xmlNode * node,const char * name)692 is_node(xmlNode *node, const char *name)
693 {
694 return node->type == XML_ELEMENT_NODE &&
695 xmlStrEqual(node->name, (const xmlChar*)name);
696 }
697
698 /* return a copy of the text content from the first text node of this node */
699 static char *
extract_text(xmlNode * node)700 extract_text(xmlNode *node)
701 {
702 xmlNode *n;
703
704 for (n = node->children; n; n = n->next) {
705 if (n->type == XML_TEXT_NODE)
706 return (char *)xmlStrdup(n->content);
707 }
708 return NULL;
709 }
710
711 static bool
parse_config_item(struct rxkb_context * ctx,xmlNode * parent,char ** name,char ** description,char ** brief,char ** vendor)712 parse_config_item(struct rxkb_context *ctx,
713 xmlNode *parent,
714 char **name,
715 char **description,
716 char **brief,
717 char **vendor)
718 {
719 xmlNode *node = NULL;
720 xmlNode *ci = NULL;
721
722 for (ci = parent->children; ci; ci = ci->next) {
723 if (is_node(ci, "configItem")) {
724 *name = NULL;
725 *description = NULL;
726 *brief = NULL;
727 *vendor = NULL;
728
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 */
740 }
741
742 if (!*name || !strlen(*name)) {
743 log_err(ctx, "xml:%d: missing required element 'name'\n",
744 ci->line);
745 free(*name);
746 free(*description);
747 free(*brief);
748 free(*vendor);
749 return false;
750 }
751
752 return true; /* only one configItem allowed in the dtd */
753 }
754 }
755
756 return false;
757 }
758
759 static void
parse_model(struct rxkb_context * ctx,xmlNode * model,enum rxkb_popularity popularity)760 parse_model(struct rxkb_context *ctx, xmlNode *model,
761 enum rxkb_popularity popularity)
762 {
763 char *name, *description, *brief, *vendor;
764
765 if (parse_config_item(ctx, model, &name, &description, &brief, &vendor)) {
766 struct rxkb_model *m;
767
768 list_for_each(m, &ctx->models, base.link) {
769 if (streq(m->name, name)) {
770 free(name);
771 free(description);
772 free(brief);
773 free(vendor);
774 return;
775 }
776 }
777
778 /* new model */
779 m = rxkb_model_create(&ctx->base);
780 m->name = name;
781 m->description = description;
782 m->vendor = vendor;
783 m->popularity = popularity;
784 list_append(&ctx->models, &m->base.link);
785 }
786 }
787
788 static void
parse_model_list(struct rxkb_context * ctx,xmlNode * model_list,enum rxkb_popularity popularity)789 parse_model_list(struct rxkb_context *ctx, xmlNode *model_list,
790 enum rxkb_popularity popularity)
791 {
792 xmlNode *node = NULL;
793
794 for (node = model_list->children; node; node = node->next) {
795 if (is_node(node, "model"))
796 parse_model(ctx, node, popularity);
797 }
798 }
799
800 static void
parse_language_list(xmlNode * language_list,struct rxkb_layout * layout)801 parse_language_list(xmlNode *language_list, struct rxkb_layout *layout)
802 {
803 xmlNode *node = NULL;
804 struct rxkb_iso639_code *code;
805
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;
810
811 parent = &layout->base;
812 code = rxkb_iso639_code_create(parent);
813 code->code = str;
814 list_append(&layout->iso639s, &code->base.link);
815 }
816 }
817 }
818
819 static void
parse_country_list(xmlNode * country_list,struct rxkb_layout * layout)820 parse_country_list(xmlNode *country_list, struct rxkb_layout *layout)
821 {
822 xmlNode *node = NULL;
823 struct rxkb_iso3166_code *code;
824
825 for (node = country_list->children; node; node = node->next) {
826 if (is_node(node, "iso3166Id")) {
827 char *str = extract_text(node);
828 struct rxkb_object *parent;
829
830 parent = &layout->base;
831 code = rxkb_iso3166_code_create(parent);
832 code->code = str;
833 list_append(&layout->iso3166s, &code->base.link);
834 }
835 }
836 }
837
838 static void
parse_variant(struct rxkb_context * ctx,struct rxkb_layout * l,xmlNode * variant,enum rxkb_popularity popularity)839 parse_variant(struct rxkb_context *ctx, struct rxkb_layout *l,
840 xmlNode *variant, enum rxkb_popularity popularity)
841 {
842 xmlNode *ci;
843 char *name, *description, *brief, *vendor;
844
845 if (parse_config_item(ctx, variant, &name, &description, &brief, &vendor)) {
846 struct rxkb_layout *v;
847 bool exists = false;
848
849 list_for_each(v, &ctx->layouts, base.link) {
850 if (streq(v->name, name) && streq(v->name, l->name)) {
851 exists = true;
852 break;
853 }
854 }
855
856 if (!exists) {
857 v = rxkb_layout_create(&ctx->base);
858 list_init(&v->iso639s);
859 list_init(&v->iso3166s);
860 v->name = strdup(l->name);
861 v->variant = name;
862 v->description = description;
863 v->brief = brief;
864 v->popularity = popularity;
865 list_append(&ctx->layouts, &v->base.link);
866
867 for (ci = variant->children; ci; ci = ci->next) {
868 xmlNode *node;
869
870 if (!is_node(ci, "configItem"))
871 continue;
872
873 for (node = ci->children; node; node = node->next) {
874 if (is_node(node, "languageList"))
875 parse_language_list(node, v);
876 if (is_node(node, "countryList"))
877 parse_country_list(node, v);
878 }
879 }
880 } else {
881 free(name);
882 free(description);
883 free(brief);
884 free(vendor);
885 }
886 }
887 }
888
889 static void
parse_variant_list(struct rxkb_context * ctx,struct rxkb_layout * l,xmlNode * variant_list,enum rxkb_popularity popularity)890 parse_variant_list(struct rxkb_context *ctx, struct rxkb_layout *l,
891 xmlNode *variant_list, enum rxkb_popularity popularity)
892 {
893 xmlNode *node = NULL;
894
895 for (node = variant_list->children; node; node = node->next) {
896 if (is_node(node, "variant"))
897 parse_variant(ctx, l, node, popularity);
898 }
899 }
900
901 static void
parse_layout(struct rxkb_context * ctx,xmlNode * layout,enum rxkb_popularity popularity)902 parse_layout(struct rxkb_context *ctx, xmlNode *layout,
903 enum rxkb_popularity popularity)
904 {
905 char *name, *description, *brief, *vendor;
906 struct rxkb_layout *l;
907 xmlNode *node = NULL;
908 bool exists = false;
909
910 if (!parse_config_item(ctx, layout, &name, &description, &brief, &vendor))
911 return;
912
913 list_for_each(l, &ctx->layouts, base.link) {
914 if (streq(l->name, name) && l->variant == NULL) {
915 exists = true;
916 break;
917 }
918 }
919
920 if (!exists) {
921 l = rxkb_layout_create(&ctx->base);
922 list_init(&l->iso639s);
923 list_init(&l->iso3166s);
924 l->name = name;
925 l->variant = NULL;
926 l->description = description;
927 l->brief = brief;
928 l->popularity = popularity;
929 list_append(&ctx->layouts, &l->base.link);
930 } else {
931 free(name);
932 free(description);
933 free(brief);
934 free(vendor);
935 }
936
937 for (node = layout->children; node; node = node->next) {
938 if (is_node(node, "variantList")) {
939 parse_variant_list(ctx, l, node, popularity);
940 }
941 if (!exists && is_node(node, "configItem")) {
942 xmlNode *ll;
943 for (ll = node->children; ll; ll = ll->next) {
944 if (is_node(ll, "languageList"))
945 parse_language_list(ll, l);
946 if (is_node(ll, "countryList"))
947 parse_country_list(ll, l);
948 }
949 }
950 }
951 }
952
953 static void
parse_layout_list(struct rxkb_context * ctx,xmlNode * layout_list,enum rxkb_popularity popularity)954 parse_layout_list(struct rxkb_context *ctx, xmlNode *layout_list,
955 enum rxkb_popularity popularity)
956 {
957 xmlNode *node = NULL;
958
959 for (node = layout_list->children; node; node = node->next) {
960 if (is_node(node, "layout"))
961 parse_layout(ctx, node, popularity);
962 }
963 }
964
965 static void
parse_option(struct rxkb_context * ctx,struct rxkb_option_group * group,xmlNode * option,enum rxkb_popularity popularity)966 parse_option(struct rxkb_context *ctx, struct rxkb_option_group *group,
967 xmlNode *option, enum rxkb_popularity popularity)
968 {
969 char *name, *description, *brief, *vendor;
970
971 if (parse_config_item(ctx, option, &name, &description, &brief, &vendor)) {
972 struct rxkb_option *o;
973
974 list_for_each(o, &group->options, base.link) {
975 if (streq(o->name, name)) {
976 free(name);
977 free(description);
978 free(brief);
979 free(vendor);
980 return;
981 }
982 }
983
984 o = rxkb_option_create(&group->base);
985 o->name = name;
986 o->description = description;
987 o->popularity = popularity;
988 list_append(&group->options, &o->base.link);
989 }
990 }
991
992 static void
parse_group(struct rxkb_context * ctx,xmlNode * group,enum rxkb_popularity popularity)993 parse_group(struct rxkb_context *ctx, xmlNode *group,
994 enum rxkb_popularity popularity)
995 {
996 char *name, *description, *brief, *vendor;
997 struct rxkb_option_group *g;
998 xmlNode *node = NULL;
999 xmlChar *multiple;
1000 bool exists = false;
1001
1002 if (!parse_config_item(ctx, group, &name, &description, &brief, &vendor))
1003 return;
1004
1005 list_for_each(g, &ctx->option_groups, base.link) {
1006 if (streq(g->name, name)) {
1007 exists = true;
1008 break;
1009 }
1010 }
1011
1012 if (!exists) {
1013 g = rxkb_option_group_create(&ctx->base);
1014 g->name = name;
1015 g->description = description;
1016 g->popularity = popularity;
1017
1018 multiple = xmlGetProp(group, (const xmlChar*)"allowMultipleSelection");
1019 if (multiple && xmlStrEqual(multiple, (const xmlChar*)"true"))
1020 g->allow_multiple = true;
1021 xmlFree(multiple);
1022
1023 list_init(&g->options);
1024 list_append(&ctx->option_groups, &g->base.link);
1025 } else {
1026 free(name);
1027 free(description);
1028 free(brief);
1029 free(vendor);
1030 }
1031
1032 for (node = group->children; node; node = node->next) {
1033 if (is_node(node, "option"))
1034 parse_option(ctx, g, node, popularity);
1035 }
1036 }
1037
1038 static void
parse_option_list(struct rxkb_context * ctx,xmlNode * option_list,enum rxkb_popularity popularity)1039 parse_option_list(struct rxkb_context *ctx, xmlNode *option_list,
1040 enum rxkb_popularity popularity)
1041 {
1042 xmlNode *node = NULL;
1043
1044 for (node = option_list->children; node; node = node->next) {
1045 if (is_node(node, "group"))
1046 parse_group(ctx, node, popularity);
1047 }
1048 }
1049
1050 static void
parse_rules_xml(struct rxkb_context * ctx,xmlNode * root,enum rxkb_popularity popularity)1051 parse_rules_xml(struct rxkb_context *ctx, xmlNode *root,
1052 enum rxkb_popularity popularity)
1053 {
1054 xmlNode *node = NULL;
1055
1056 for (node = root->children; node; node = node->next) {
1057 if (is_node(node, "modelList"))
1058 parse_model_list(ctx, node, popularity);
1059 else if (is_node(node, "layoutList"))
1060 parse_layout_list(ctx, node, popularity);
1061 else if (is_node(node, "optionList"))
1062 parse_option_list(ctx, node, popularity);
1063 }
1064 }
1065
1066 static void
1067 ATTR_PRINTF(2, 0)
xml_error_func(void * ctx,const char * msg,...)1068 xml_error_func(void *ctx, const char *msg, ...)
1069 {
1070 static char buf[PATH_MAX];
1071 static int slen = 0;
1072 va_list args;
1073 int rc;
1074
1075 /* libxml2 prints IO errors from bad includes paths by
1076 * calling the error function once per word. So we get to
1077 * re-assemble the message here and print it when we get
1078 * the line break. My enthusiasm about this is indescribable.
1079 */
1080 va_start(args, msg);
1081 rc = vsnprintf(&buf[slen], sizeof(buf) - slen, msg, args);
1082 va_end(args);
1083
1084 /* This shouldn't really happen */
1085 if (rc < 0) {
1086 log_err(ctx, "+++ out of cheese error. redo from start +++\n");
1087 slen = 0;
1088 memset(buf, 0, sizeof(buf));
1089 return;
1090 }
1091
1092 slen += rc;
1093 if (slen >= (int)sizeof(buf)) {
1094 /* truncated, let's flush this */
1095 buf[sizeof(buf) - 1] = '\n';
1096 slen = sizeof(buf);
1097 }
1098
1099 /* We're assuming here that the last character is \n. */
1100 if (buf[slen - 1] == '\n') {
1101 log_err(ctx, "%s", buf);
1102 memset(buf, 0, sizeof(buf));
1103 slen = 0;
1104 }
1105 }
1106
1107 static bool
validate(struct rxkb_context * ctx,xmlDoc * doc)1108 validate(struct rxkb_context *ctx, xmlDoc *doc)
1109 {
1110 bool success = false;
1111 xmlValidCtxt *dtdvalid = NULL;
1112 xmlDtd *dtd = NULL;
1113 xmlParserInputBufferPtr buf = NULL;
1114 /* This is a modified version of the xkeyboard-config xkb.dtd. That one
1115 * requires modelList, layoutList and optionList, we
1116 * allow for any of those to be missing.
1117 */
1118 const char dtdstr[] =
1119 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1120 "<!ELEMENT xkbConfigRegistry (modelList?, layoutList?, optionList?)>\n"
1121 "<!ATTLIST xkbConfigRegistry version CDATA \"1.1\">\n"
1122 "<!ELEMENT modelList (model*)>\n"
1123 "<!ELEMENT model (configItem)>\n"
1124 "<!ELEMENT layoutList (layout*)>\n"
1125 "<!ELEMENT layout (configItem, variantList?)>\n"
1126 "<!ELEMENT optionList (group*)>\n"
1127 "<!ELEMENT variantList (variant*)>\n"
1128 "<!ELEMENT variant (configItem)>\n"
1129 "<!ELEMENT group (configItem, option*)>\n"
1130 "<!ATTLIST group allowMultipleSelection (true|false) \"false\">\n"
1131 "<!ELEMENT option (configItem)>\n"
1132 "<!ELEMENT configItem (name, shortDescription?, description?, vendor?, countryList?, languageList?, hwList?)>\n"
1133 "<!ATTLIST configItem popularity (standard|exotic) \"standard\">\n"
1134 "<!ELEMENT name (#PCDATA)>\n"
1135 "<!ELEMENT shortDescription (#PCDATA)>\n"
1136 "<!ELEMENT description (#PCDATA)>\n"
1137 "<!ELEMENT vendor (#PCDATA)>\n"
1138 "<!ELEMENT countryList (iso3166Id+)>\n"
1139 "<!ELEMENT iso3166Id (#PCDATA)>\n"
1140 "<!ELEMENT languageList (iso639Id+)>\n"
1141 "<!ELEMENT iso639Id (#PCDATA)>\n"
1142 "<!ELEMENT hwList (hwId+)>\n"
1143 "<!ELEMENT hwId (#PCDATA)>\n";
1144
1145 /* Note: do not use xmlParserInputBufferCreateStatic, it generates random
1146 * DTD validity errors for unknown reasons */
1147 buf = xmlParserInputBufferCreateMem(dtdstr, sizeof(dtdstr),
1148 XML_CHAR_ENCODING_UTF8);
1149 if (!buf)
1150 return false;
1151
1152 dtd = xmlIOParseDTD(NULL, buf, XML_CHAR_ENCODING_UTF8);
1153 if (!dtd) {
1154 log_err(ctx, "Failed to load DTD\n");
1155 return false;
1156 }
1157
1158 dtdvalid = xmlNewValidCtxt();
1159 if (xmlValidateDtd(dtdvalid, doc, dtd))
1160 success = true;
1161
1162 if (dtd)
1163 xmlFreeDtd(dtd);
1164 if (dtdvalid)
1165 xmlFreeValidCtxt(dtdvalid);
1166
1167 return success;
1168 }
1169
1170 static bool
parse(struct rxkb_context * ctx,const char * path,enum rxkb_popularity popularity)1171 parse(struct rxkb_context *ctx, const char *path,
1172 enum rxkb_popularity popularity)
1173 {
1174 bool success = false;
1175 xmlDoc *doc = NULL;
1176 xmlNode *root = NULL;
1177
1178 if (!check_eaccess(path, R_OK))
1179 return false;
1180
1181 LIBXML_TEST_VERSION
1182
1183 xmlSetGenericErrorFunc(ctx, xml_error_func);
1184
1185 doc = xmlParseFile(path);
1186 if (!doc)
1187 return false;
1188
1189 if (!validate(ctx, doc)) {
1190 log_err(ctx, "XML error: failed to validate document at %s\n", path);
1191 goto error;
1192 }
1193
1194 root = xmlDocGetRootElement(doc);
1195 parse_rules_xml(ctx, root, popularity);
1196
1197 success = true;
1198 error:
1199 xmlFreeDoc(doc);
1200 xmlCleanupParser();
1201
1202 return success;
1203 }
1204