1 /************************************************************
2 Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
4 Permission to use, copy, modify, and distribute this
5 software and its documentation for any purpose and without
6 fee is hereby granted, provided that the above copyright
7 notice appear in all copies and that both that copyright
8 notice and this permission notice appear in supporting
9 documentation, and that the name of Silicon Graphics not be
10 used in advertising or publicity pertaining to distribution
11 of the software without specific prior written permission.
12 Silicon Graphics makes no representation about the suitability
13 of this software for any purpose. It is provided "as is"
14 without any express or implied warranty.
16 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
23 THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 ********************************************************/
33 #define DFLT_LINE_SIZE 128
38 char buf[DFLT_LINE_SIZE];
43 input_line_init(struct input_line *line)
45 line->size = DFLT_LINE_SIZE;
47 line->line = line->buf;
51 input_line_deinit(struct input_line *line)
53 if (line->line != line->buf)
56 line->size = DFLT_LINE_SIZE;
57 line->line = line->buf;
61 input_line_add_char(struct input_line *line, int ch)
63 if (line->offset >= line->size) {
64 if (line->line == line->buf) {
65 line->line = malloc(line->size * 2);
66 memcpy(line->line, line->buf, line->size);
69 line->line = realloc(line->line, line->size * 2);
75 line->line[line->offset++] = ch;
80 input_line_get(FILE *file, struct input_line *line)
83 bool end_of_file = false;
88 while (!end_of_file && line->offset == 0) {
89 space_pending = slash_pending = in_comment = false;
91 while ((ch = getc(file)) != '\n' && ch != EOF) {
110 slash_pending = false;
113 slash_pending = true;
121 input_line_add_char(line, ' ');
122 space_pending = false;
125 input_line_add_char(line, '/');
126 slash_pending = false;
130 while (isspace(ch) && ch != '\n' && ch != EOF)
136 if (ch != '\n' && line->offset > 0)
137 space_pending = true;
143 input_line_add_char(line, ' ');
144 space_pending = false;
148 if (line->offset != 0) {
149 WARN("The '!' is legal only at start of line\n");
150 ACTION("Line containing '!' ignored\n");
156 input_line_add_char(line, ch);
164 if (line->offset == 0 && end_of_file)
167 input_line_add_char(line, '\0');
171 /***====================================================================***/
174 /* "Parts" - the MLVO which rules file maps to components. */
181 ((1 << MODEL) | (1 << LAYOUT) | (1 << VARIANT) | (1 << OPTION))
191 #define COMPONENT_MASK \
192 ((1 << KEYCODES) | (1 << SYMBOLS) | (1 << TYPES) | (1 << COMPAT) | \
193 (1 << GEOMETRY) | (1 << KEYMAP))
198 static const char *cname[] = {
201 [VARIANT] = "variant",
204 [KEYCODES] = "keycodes",
205 [SYMBOLS] = "symbols",
208 [GEOMETRY] = "geometry",
221 const char *layout[XkbNumKbdGroups + 1];
222 const char *variant[XkbNumKbdGroups + 1];
227 /* Sequential id for the mappings. */
242 struct describe_vars {
245 struct var_desc *desc;
255 RULE_FLAG_PENDING_MATCH = (1L << 1),
256 RULE_FLAG_OPTION = (1L << 2),
257 RULE_FLAG_APPEND = (1L << 3),
258 RULE_FLAG_NORMAL = (1L << 4),
282 struct describe_vars models;
283 struct describe_vars layouts;
284 struct describe_vars variants;
285 struct describe_vars options;
293 struct group *groups;
296 /***====================================================================***/
299 * Resolve numeric index, such as "[4]" in layout[4]. Missing index
303 get_index(char *str, int *ndx)
305 int empty = 0, consumed = 0, num;
307 sscanf(str, "[%n%d]%n", &empty, &num, &consumed);
311 } else if (empty > 0) {
321 * Match a mapping line which opens a rule, e.g:
322 * ! model layout[4] variant[4] = symbols geometry
323 * Which will be followed by lines such as:
324 * * ben basic = +in(ben):4 nec(pc98)
325 * So if the MLVO matches the LHS of some line, we'll get the components
327 * In this example, we will get for the second and fourth columns:
328 * mapping->map[1] = {.word = LAYOUT, .index = 4}
329 * mapping->map[3] = {.word = SYMBOLS, .index = 0}
332 match_mapping_line(struct input_line *line, struct mapping *mapping)
335 char *str = &line->line[1];
336 unsigned present = 0, layout_ndx_present = 0, variant_ndx_present = 0;
344 * Remember the last sequential mapping id (incremented if the match
347 tmp = mapping->number;
348 memset(mapping, 0, sizeof(*mapping));
349 mapping->number = tmp;
351 while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
355 if (strcmp(tok, "=") == 0)
358 for (i = 0; i < MAX_WORDS; i++) {
359 len = strlen(cname[i]);
361 if (strncmp(cname[i], tok, len) == 0) {
362 if (strlen(tok) > len) {
363 char *end = get_index(tok + len, &ndx);
365 if ((i != LAYOUT && i != VARIANT) ||
366 *end != '\0' || ndx == -1) {
367 WARN("Illegal %s index: %d\n", cname[i], ndx);
368 WARN("Can only index layout and variant\n");
372 if (ndx < 1 || ndx > XkbNumKbdGroups) {
373 WARN("Illegal %s index: %d\n", cname[i], ndx);
374 WARN("Index must be in range 1..%d\n", XkbNumKbdGroups);
383 if (present & (1 << i)) {
384 if ((i == LAYOUT && layout_ndx_present & (1 << ndx)) ||
385 (i == VARIANT && variant_ndx_present & (1 << ndx))) {
386 WARN("Component \"%s\" listed twice\n", tok);
387 ACTION("Second definition ignored\n");
394 layout_ndx_present |= 1 << ndx;
396 variant_ndx_present |= 1 << ndx;
398 mapping->map[mapping->num_maps].word = i;
399 mapping->map[mapping->num_maps].index = ndx;
406 WARN("Unknown component \"%s\"\n", tok);
411 if ((present & PART_MASK) == 0) {
412 WARN("Mapping needs at least one MLVO part\n");
413 ACTION("Illegal mapping ignored\n");
414 mapping->num_maps = 0;
418 if ((present & COMPONENT_MASK) == 0) {
419 WARN("Mapping needs at least one component\n");
420 ACTION("Illegal mapping ignored\n");
421 mapping->num_maps = 0;
425 if (((present & COMPONENT_MASK) & (1 << KEYMAP)) &&
426 ((present & COMPONENT_MASK) != (1 << KEYMAP))) {
427 WARN("Keymap cannot appear with other components\n");
428 ACTION("Illegal mapping ignored\n");
429 mapping->num_maps = 0;
437 * Match a line such as:
438 * ! $pcmodels = pc101 pc102 pc104 pc105
441 match_group_line(struct input_line *line, struct group *group)
444 char *name = strchr(line->line, '$');
445 char *words = strchr(name, ' ');
452 for (; *words; words++) {
453 if (*words != '=' && *words != ' ')
460 group->name = strdup(name);
461 group->words = strdup(words);
463 words = group->words;
464 for (i = 1; *words; words++) {
476 /* Match lines following a mapping (see match_mapping_line comment). */
478 match_rule_line(struct input_line *line, struct mapping *mapping,
485 const char *names[MAX_WORDS] = { NULL };
487 if (mapping->num_maps == 0) {
488 WARN("Must have a mapping before first line of data\n");
489 ACTION("Illegal line of data ignored\n");
495 for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL; nread++) {
498 if (strcmp(tok, "=") == 0) {
503 if (nread > mapping->num_maps) {
504 WARN("Too many words on a line\n");
505 ACTION("Extra word \"%s\" ignored\n", tok);
509 names[mapping->map[nread].word] = tok;
510 if (*tok == '+' || *tok == '|')
514 if (nread < mapping->num_maps) {
515 WARN("Too few words on a line: %s\n", line->line);
516 ACTION("line ignored\n");
521 rule->number = mapping->number;
524 rule->flags |= RULE_FLAG_OPTION;
526 rule->flags |= RULE_FLAG_APPEND;
528 rule->flags |= RULE_FLAG_NORMAL;
530 rule->model = uDupString(names[MODEL]);
531 rule->layout = uDupString(names[LAYOUT]);
532 rule->variant = uDupString(names[VARIANT]);
533 rule->option = uDupString(names[OPTION]);
535 rule->keycodes = uDupString(names[KEYCODES]);
536 rule->symbols = uDupString(names[SYMBOLS]);
537 rule->types = uDupString(names[TYPES]);
538 rule->compat = uDupString(names[COMPAT]);
539 rule->keymap = uDupString(names[KEYMAP]);
541 rule->layout_num = rule->variant_num = 0;
542 for (i = 0; i < nread; i++) {
543 if (mapping->map[i].index) {
544 if (mapping->map[i].word == LAYOUT)
545 rule->layout_num = mapping->map[i].index;
546 if (mapping->map[i].word == VARIANT)
547 rule->variant_num = mapping->map[i].index;
555 match_line(struct input_line *line, struct mapping *mapping,
556 struct rule *rule, struct group *group)
558 if (line->line[0] != '!')
559 return match_rule_line(line, mapping, rule);
561 if (line->line[1] == '$' || (line->line[1] == ' ' && line->line[2] == '$'))
562 return match_group_line(line, group);
564 match_mapping_line(line, mapping);
569 squeeze_spaces(char *p1)
573 for (p2 = p1; *p2; p2++) {
583 * Expand the layout and variant of a var_defs and remove extraneous spaces.
584 * If there's one layout/variant, it is kept in .layout[0]/.variant[0], else
585 * is kept in [1], [2] and so on, and [0] remains empty.
586 * For example, this var_defs:
588 * .layout = "us,il,ru,ca"
589 * .variant = ",,,multix"
590 * .options = "grp:alts_toggle, ctrl:nocaps, compose:rwin"
591 * Is expanded into this multi_defs:
593 * .layout = {NULL, "us", "il", "ru", "ca"},
594 * .variant = {NULL, "", "", "", "multix"},
595 * .options = "grp:alts_toggle,ctrl:nocaps,compose:rwin"
598 make_multi_defs(struct multi_defs *mdefs, struct var_defs *defs)
603 memset(mdefs, 0, sizeof(*mdefs));
606 mdefs->model = defs->model;
610 mdefs->options = strdup(defs->options);
611 if (mdefs->options == NULL)
614 squeeze_spaces(mdefs->options);
618 if (!strchr(defs->layout, ',')) {
619 mdefs->layout[0] = defs->layout;
622 p = strdup(defs->layout);
627 mdefs->layout[1] = p;
629 for (i = 2; i <= XkbNumKbdGroups; i++) {
630 if ((p = strchr(p, ','))) {
632 mdefs->layout[i] = p;
639 if (p && (p = strchr(p, ',')))
645 if (!strchr(defs->variant, ',')) {
646 mdefs->variant[0] = defs->variant;
649 p = strdup(defs->variant);
654 mdefs->variant[1] = p;
656 for (i = 2; i <= XkbNumKbdGroups; i++) {
657 if ((p = strchr(p, ','))) {
659 mdefs->variant[i] = p;
665 if (p && (p = strchr(p, ',')))
674 free_multi_defs(struct multi_defs *defs)
678 * See make_multi_defs comment for the hack; the same strdup'd
679 * string is split among the indexes, but the one in [0] is const.
681 free(UNCONSTIFY(defs->layout[1]));
682 free(UNCONSTIFY(defs->variant[1]));
685 /* See apply_rule below. */
687 apply(const char *src, char **dst)
695 if (*src == '+' || *src == '!') {
697 ret = asprintf(dst, "%s%s", *dst, src);
702 else if (*dst == NULL) {
708 * Add the info from the matching rule to the resulting
709 * xkb_component_names. If we already had a match for something
710 * (e.g. keycodes), and the rule is not an appending one (e.g.
711 * +whatever), than we don't override but drop the new one.
714 apply_rule(struct rule *rule, struct xkb_component_names *kccgst)
716 /* Clear the flag because it's applied. */
717 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
719 apply(rule->keycodes, &kccgst->keycodes);
720 apply(rule->symbols, &kccgst->symbols);
721 apply(rule->types, &kccgst->types);
722 apply(rule->compat, &kccgst->compat);
723 apply(rule->keymap, &kccgst->keymap);
727 CheckGroup(struct rules *rules, const char *group_name, const char *name)
733 for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
734 if (! strcmp(group->name, group_name)) {
738 if (i == rules->num_groups)
740 for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
741 if (! strcmp(p, name)) {
749 MatchOneOf(char *wanted, char *vals_defined)
752 int want_len = strlen(wanted);
754 for (str = vals_defined, next = NULL; str != NULL; str = next) {
756 next = strchr(str, ',');
765 if (len == want_len && strncmp(wanted, str, len) == 0)
773 XkbRF_CheckApplyRule(struct rule *rule, struct multi_defs *mdefs,
774 struct xkb_component_names *names, struct rules *rules)
776 bool pending = false;
778 if (rule->model != NULL) {
779 if(mdefs->model == NULL)
781 if (strcmp(rule->model, "*") == 0) {
784 if (rule->model[0] == '$') {
785 if (!CheckGroup(rules, rule->model, mdefs->model))
788 if (strcmp(rule->model, mdefs->model) != 0)
793 if (rule->option != NULL) {
794 if (mdefs->options == NULL)
796 if ((!MatchOneOf(rule->option,mdefs->options)))
800 if (rule->layout != NULL) {
801 if(mdefs->layout[rule->layout_num] == NULL ||
802 *mdefs->layout[rule->layout_num] == '\0')
804 if (strcmp(rule->layout, "*") == 0) {
807 if (rule->layout[0] == '$') {
808 if (!CheckGroup(rules, rule->layout,
809 mdefs->layout[rule->layout_num]))
812 if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
817 if (rule->variant != NULL) {
818 if (mdefs->variant[rule->variant_num] == NULL ||
819 *mdefs->variant[rule->variant_num] == '\0')
821 if (strcmp(rule->variant, "*") == 0) {
824 if (rule->variant[0] == '$') {
825 if (!CheckGroup(rules, rule->variant,
826 mdefs->variant[rule->variant_num]))
829 if (strcmp(rule->variant,
830 mdefs->variant[rule->variant_num]) != 0)
836 rule->flags |= RULE_FLAG_PENDING_MATCH;
839 /* exact match, apply it now */
840 apply_rule(rule, names);
845 XkbRF_ClearPartialMatches(struct rules *rules)
850 for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
851 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
856 XkbRF_ApplyPartialMatches(struct rules *rules,
857 struct xkb_component_names *names)
862 for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
863 if ((rule->flags & RULE_FLAG_PENDING_MATCH)==0)
865 apply_rule(rule, names);
870 XkbRF_CheckApplyRules(struct rules *rules, struct multi_defs *mdefs,
871 struct xkb_component_names *names, unsigned int flags)
877 for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
878 if ((rule->flags & flags) != flags)
880 skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
881 if (skip && !(flags & RULE_FLAG_OPTION)) {
882 for ( ;(i < rules->num_rules) && (rule->number == skip);
889 /***====================================================================***/
892 XkbRF_SubstituteVars(char *name, struct multi_defs *mdefs)
894 char *str, *outstr, *orig, *var;
902 str= strchr(name,'%');
909 if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
918 str = get_index(var + 1, &ndx);
920 str = strchr(str,'%');
923 if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
924 len+= strlen(mdefs->layout[ndx])+extra_len;
925 else if ((*var=='m')&&mdefs->model)
926 len+= strlen(mdefs->model)+extra_len;
927 else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
928 len+= strlen(mdefs->variant[ndx])+extra_len;
929 if ((pfx=='(')&&(*str==')')) {
932 str= strchr(&str[0],'%');
934 name = malloc(len + 1);
943 if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
953 str = get_index(var + 1, &ndx);
957 if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
958 if (pfx) *outstr++= pfx;
959 strcpy(outstr,mdefs->layout[ndx]);
960 outstr+= strlen(mdefs->layout[ndx]);
961 if (sfx) *outstr++= sfx;
963 else if ((*var=='m')&&(mdefs->model)) {
964 if (pfx) *outstr++= pfx;
965 strcpy(outstr,mdefs->model);
966 outstr+= strlen(mdefs->model);
967 if (sfx) *outstr++= sfx;
969 else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
970 if (pfx) *outstr++= pfx;
971 strcpy(outstr,mdefs->variant[ndx]);
972 outstr+= strlen(mdefs->variant[ndx]);
973 if (sfx) *outstr++= sfx;
975 if ((pfx=='(')&&(*str==')'))
988 /***====================================================================***/
991 XkbcRF_GetComponents(struct rules *rules, struct var_defs *defs,
992 struct xkb_component_names *names)
994 struct multi_defs mdefs;
996 make_multi_defs(&mdefs, defs);
998 memset(names, 0, sizeof(struct xkb_component_names));
999 XkbRF_ClearPartialMatches(rules);
1000 XkbRF_CheckApplyRules(rules, &mdefs, names, RULE_FLAG_NORMAL);
1001 XkbRF_ApplyPartialMatches(rules, names);
1002 XkbRF_CheckApplyRules(rules, &mdefs, names, RULE_FLAG_APPEND);
1003 XkbRF_ApplyPartialMatches(rules, names);
1004 XkbRF_CheckApplyRules(rules, &mdefs, names, RULE_FLAG_OPTION);
1005 XkbRF_ApplyPartialMatches(rules, names);
1007 names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs);
1008 names->symbols = XkbRF_SubstituteVars(names->symbols, &mdefs);
1009 names->types = XkbRF_SubstituteVars(names->types, &mdefs);
1010 names->compat = XkbRF_SubstituteVars(names->compat, &mdefs);
1011 names->keymap = XkbRF_SubstituteVars(names->keymap, &mdefs);
1013 free_multi_defs(&mdefs);
1015 return (names->keycodes && names->symbols && names->types &&
1016 names->compat) || names->keymap;
1019 static struct rule *
1020 XkbcRF_AddRule(struct rules *rules)
1022 if (rules->sz_rules<1) {
1023 rules->sz_rules= 16;
1024 rules->num_rules= 0;
1025 rules->rules = calloc(rules->sz_rules, sizeof(*rules->rules));
1027 else if (rules->num_rules>=rules->sz_rules) {
1028 rules->sz_rules*= 2;
1029 rules->rules = realloc(rules->rules,
1030 rules->sz_rules * sizeof(*rules->rules));
1032 if (!rules->rules) {
1033 rules->sz_rules= rules->num_rules= 0;
1036 memset(&rules->rules[rules->num_rules], 0, sizeof(*rules->rules));
1037 return &rules->rules[rules->num_rules++];
1040 static struct group *
1041 XkbcRF_AddGroup(struct rules *rules)
1043 if (rules->sz_groups<1) {
1044 rules->sz_groups= 16;
1045 rules->num_groups= 0;
1046 rules->groups = calloc(rules->sz_groups, sizeof(*rules->groups));
1048 else if (rules->num_groups >= rules->sz_groups) {
1049 rules->sz_groups *= 2;
1050 rules->groups = realloc(rules->groups,
1051 rules->sz_groups * sizeof(*rules->groups));
1053 if (!rules->groups) {
1054 rules->sz_groups= rules->num_groups= 0;
1058 memset(&rules->groups[rules->num_groups], 0, sizeof(*rules->groups));
1059 return &rules->groups[rules->num_groups++];
1062 static struct rules *
1063 XkbcRF_LoadRules(FILE *file)
1065 struct input_line line;
1066 struct mapping mapping;
1067 struct rule trule, *rule;
1068 struct group tgroup, *group;
1069 struct rules *rules;
1071 rules = calloc(1, sizeof(*rules));
1075 memset(&mapping, 0, sizeof(mapping));
1076 memset(&tgroup, 0, sizeof(tgroup));
1077 input_line_init(&line);
1079 while (input_line_get(file, &line)) {
1080 if (match_line(&line, &mapping, &trule, &tgroup)) {
1081 if (tgroup.number) {
1082 if ((group= XkbcRF_AddGroup(rules))!=NULL) {
1084 memset(&tgroup, 0, sizeof(tgroup));
1087 if ((rule= XkbcRF_AddRule(rules))!=NULL) {
1089 memset(&trule, 0, sizeof(trule));
1095 input_line_deinit(&line);
1100 XkbRF_ClearVarDescriptions(struct describe_vars *var)
1104 for (i=0;i<var->num_desc;i++) {
1105 free(var->desc[i].name);
1106 free(var->desc[i].desc);
1107 var->desc[i].name= var->desc[i].desc= NULL;
1114 XkbcRF_Free(struct rules *rules)
1118 struct group *group;
1122 XkbRF_ClearVarDescriptions(&rules->models);
1123 XkbRF_ClearVarDescriptions(&rules->layouts);
1124 XkbRF_ClearVarDescriptions(&rules->variants);
1125 XkbRF_ClearVarDescriptions(&rules->options);
1127 for (i=0, rule = rules->rules; i < rules->num_rules && rules; i++, rule++) {
1130 free(rule->variant);
1132 free(rule->keycodes);
1133 free(rule->symbols);
1140 for (i=0, group = rules->groups; i < rules->num_groups && group; i++, group++) {
1144 free(rules->groups);
1149 struct xkb_component_names *
1150 xkb_components_from_rules(struct xkb_context *ctx,
1151 const struct xkb_rule_names *rmlvo)
1156 struct rules *loaded;
1157 struct xkb_component_names *names = NULL;
1158 struct var_defs defs = {
1159 .model = rmlvo->model,
1160 .layout = rmlvo->layout,
1161 .variant = rmlvo->variant,
1162 .options = rmlvo->options,
1165 rulesFile = XkbFindFileInPath(ctx, rmlvo->rules, XkmRulesFile,
1168 ERROR("could not find \"%s\" rules in XKB path\n", rmlvo->rules);
1169 ERROR("%d include paths searched:\n",
1170 xkb_context_num_include_paths(ctx));
1171 for (i = 0; i < xkb_context_num_include_paths(ctx); i++)
1172 ERROR("\t%s\n", xkb_context_include_path_get(ctx, i));
1176 loaded = XkbcRF_LoadRules(rulesFile);
1178 ERROR("failed to load XKB rules \"%s\"\n", rulesPath);
1182 names = calloc(1, sizeof(*names));
1184 ERROR("failed to allocate XKB components\n");
1188 if (!XkbcRF_GetComponents(loaded, &defs, names)) {
1189 free(names->keymap);
1190 free(names->keycodes);
1192 free(names->compat);
1193 free(names->symbols);
1196 ERROR("no components returned from XKB rules \"%s\"\n", rulesPath);
1200 XkbcRF_Free(loaded);