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 ********************************************************/
27 #ifdef HAVE_DIX_CONFIG_H
28 #include <dix-config.h>
35 #define X_INCLUDE_STRING_H
36 #define XOS_USE_NO_LOCKING
37 #include <X11/Xos_r.h>
39 #include <X11/Xproto.h>
42 #include <X11/Xfuncs.h>
43 #include <X11/Xatom.h>
44 #include <X11/keysym.h>
50 #define XKBSRV_NEED_FILE_FUNCS
53 /***====================================================================***/
55 #define DFLT_LINE_SIZE 128
61 char buf[DFLT_LINE_SIZE];
66 InitInputLine(InputLine * line)
70 line->sz_line = DFLT_LINE_SIZE;
71 line->line = line->buf;
76 FreeInputLine(InputLine * line)
78 if (line->line != line->buf)
82 line->sz_line = DFLT_LINE_SIZE;
83 line->line = line->buf;
88 InputLineAddChar(InputLine * line, int ch)
90 if (line->num_line >= line->sz_line) {
91 if (line->line == line->buf) {
92 line->line = malloc(line->sz_line * 2);
93 memcpy(line->line, line->buf, line->sz_line);
96 line->line = realloc((char *) line->line, line->sz_line * 2);
100 line->line[line->num_line++] = ch;
104 #define ADD_CHAR(l,c) ((l)->num_line<(l)->sz_line?\
105 (int)((l)->line[(l)->num_line++]= (c)):\
106 InputLineAddChar(l,c))
109 GetInputLine(FILE * file, InputLine * line, Bool checkbang)
112 Bool endOfFile, spacePending, slashPending, inComment;
115 while ((!endOfFile) && (line->num_line == 0)) {
116 spacePending = slashPending = inComment = FALSE;
117 while (((ch = getc(file)) != '\n') && (ch != EOF)) {
119 if ((ch = getc(file)) == EOF)
132 slashPending = FALSE;
139 else if (slashPending) {
142 spacePending = FALSE;
145 slashPending = FALSE;
148 while (isspace(ch) && (ch != '\n') && (ch != EOF)) {
153 if ((ch != '\n') && (line->num_line > 0))
160 spacePending = FALSE;
162 if (checkbang && ch == '!') {
163 if (line->num_line != 0) {
164 DebugF("The '!' legal only at start of line\n");
165 DebugF("Line containing '!' ignored\n");
177 /* else line->num_line++;*/
179 if ((line->num_line == 0) && (endOfFile))
181 ADD_CHAR(line, '\0');
185 /***====================================================================***/
198 #define PART_MASK 0x000F
199 #define COMPONENT_MASK 0x03F0
201 static const char *cname[MAX_WORDS] = {
202 "model", "layout", "variant", "option",
203 "keycodes", "symbols", "types", "compat", "geometry"
206 typedef struct _RemapSpec {
215 typedef struct _FileSpec {
216 char *name[MAX_WORDS];
217 struct _FileSpec *pending;
222 char *layout[XkbNumKbdGroups + 1];
223 char *variant[XkbNumKbdGroups + 1];
225 } XkbRF_MultiDefsRec, *XkbRF_MultiDefsPtr;
227 #define NDX_BUFF_SIZE 4
229 /***====================================================================***/
232 get_index(char *str, int *ndx)
234 char ndx_buf[NDX_BUFF_SIZE];
242 end = strchr(str, ']');
247 if ((end - str) >= NDX_BUFF_SIZE) {
251 strlcpy(ndx_buf, str, 1 + end - str);
252 *ndx = atoi(ndx_buf);
257 SetUpRemap(InputLine * line, RemapSpec * remap)
260 unsigned present, l_ndx_present, v_ndx_present;
263 _Xstrtokparams strtok_buf;
266 l_ndx_present = v_ndx_present = present = 0;
267 str = &line->line[1];
269 memset((char *) remap, 0, sizeof(RemapSpec));
271 while ((tok = _XStrtok(str, " ", strtok_buf)) != NULL) {
274 if (strcmp(tok, "=") == 0)
276 for (i = 0; i < MAX_WORDS; i++) {
277 len = strlen(cname[i]);
278 if (strncmp(cname[i], tok, len) == 0) {
279 if (strlen(tok) > len) {
280 char *end = get_index(tok + len, &ndx);
282 if ((i != LAYOUT && i != VARIANT) ||
283 *end != '\0' || ndx == -1)
285 if (ndx < 1 || ndx > XkbNumKbdGroups) {
286 DebugF("Illegal %s index: %d\n", cname[i], ndx);
287 DebugF("Index must be in range 1..%d\n",
296 if (present & (1 << i)) {
297 if ((i == LAYOUT && l_ndx_present & (1 << ndx)) ||
298 (i == VARIANT && v_ndx_present & (1 << ndx))) {
299 DebugF("Component \"%s\" listed twice\n", tok);
300 DebugF("Second definition ignored\n");
306 l_ndx_present |= 1 << ndx;
308 v_ndx_present |= 1 << ndx;
309 remap->remap[remap->num_remap].word = i;
310 remap->remap[remap->num_remap++].index = ndx;
315 fprintf(stderr, "Unknown component \"%s\" ignored\n", tok);
318 if ((present & PART_MASK) == 0) {
319 unsigned mask = PART_MASK;
321 ErrorF("Mapping needs at least one of ");
322 for (i = 0; (i < MAX_WORDS); i++) {
323 if ((1L << i) & mask) {
326 DebugF("\"%s,\" ", cname[i]);
328 DebugF("or \"%s\"\n", cname[i]);
331 DebugF("Illegal mapping ignored\n");
332 remap->num_remap = 0;
335 if ((present & COMPONENT_MASK) == 0) {
336 DebugF("Mapping needs at least one component\n");
337 DebugF("Illegal mapping ignored\n");
338 remap->num_remap = 0;
346 MatchOneOf(char *wanted, char *vals_defined)
349 int want_len = strlen(wanted);
351 for (str = vals_defined, next = NULL; str != NULL; str = next) {
354 next = strchr(str, ',');
362 if ((len == want_len) && (strncmp(wanted, str, len) == 0))
368 /***====================================================================***/
371 CheckLine(InputLine * line,
372 RemapSpec * remap, XkbRF_RulePtr rule, XkbRF_GroupPtr group)
375 register int nread, i;
377 _Xstrtokparams strtok_buf;
380 if (line->line[0] == '!') {
381 if (line->line[1] == '$' ||
382 (line->line[1] == ' ' && line->line[2] == '$')) {
383 char *gname = strchr(line->line, '$');
384 char *words = strchr(gname, ' ');
389 for (; *words; words++) {
390 if (*words != '=' && *words != ' ')
395 group->name = Xstrdup(gname);
396 group->words = Xstrdup(words);
397 for (i = 1, words = group->words; *words; words++) {
407 SetUpRemap(line, remap);
412 if (remap->num_remap == 0) {
413 DebugF("Must have a mapping before first line of data\n");
414 DebugF("Illegal line of data ignored\n");
417 memset((char *) &tmp, 0, sizeof(FileSpec));
419 for (nread = 0; (tok = _XStrtok(str, " ", strtok_buf)) != NULL; nread++) {
421 if (strcmp(tok, "=") == 0) {
425 if (nread > remap->num_remap) {
426 DebugF("Too many words on a line\n");
427 DebugF("Extra word \"%s\" ignored\n", tok);
430 tmp.name[remap->remap[nread].word] = tok;
431 if (*tok == '+' || *tok == '|')
434 if (nread < remap->num_remap) {
435 DebugF("Too few words on a line: %s\n", line->line);
436 DebugF("line ignored\n");
441 rule->number = remap->number;
442 if (tmp.name[OPTION])
443 rule->flags |= XkbRF_Option;
445 rule->flags |= XkbRF_Append;
447 rule->flags |= XkbRF_Normal;
448 rule->model = Xstrdup(tmp.name[MODEL]);
449 rule->layout = Xstrdup(tmp.name[LAYOUT]);
450 rule->variant = Xstrdup(tmp.name[VARIANT]);
451 rule->option = Xstrdup(tmp.name[OPTION]);
453 rule->keycodes = Xstrdup(tmp.name[KEYCODES]);
454 rule->symbols = Xstrdup(tmp.name[SYMBOLS]);
455 rule->types = Xstrdup(tmp.name[TYPES]);
456 rule->compat = Xstrdup(tmp.name[COMPAT]);
457 rule->geometry = Xstrdup(tmp.name[GEOMETRY]);
459 rule->layout_num = rule->variant_num = 0;
460 for (i = 0; i < nread; i++) {
461 if (remap->remap[i].index) {
462 if (remap->remap[i].word == LAYOUT)
463 rule->layout_num = remap->remap[i].index;
464 if (remap->remap[i].word == VARIANT)
465 rule->variant_num = remap->remap[i].index;
472 _Concat(char *str1, char *str2)
476 if ((!str1) || (!str2))
478 len = strlen(str1) + strlen(str2) + 1;
479 str1 = realloc(str1, len * sizeof(char));
486 squeeze_spaces(char *p1)
490 for (p2 = p1; *p2; p2++) {
499 MakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs)
502 memset((char *) mdefs, 0, sizeof(XkbRF_MultiDefsRec));
503 mdefs->model = defs->model;
504 mdefs->options = Xstrdup(defs->options);
506 squeeze_spaces(mdefs->options);
509 if (!strchr(defs->layout, ',')) {
510 mdefs->layout[0] = defs->layout;
516 mdefs->layout[1] = Xstrdup(defs->layout);
517 if (mdefs->layout[1] == NULL)
519 squeeze_spaces(mdefs->layout[1]);
520 p = mdefs->layout[1];
521 for (i = 2; i <= XkbNumKbdGroups; i++) {
522 if ((p = strchr(p, ','))) {
524 mdefs->layout[i] = p;
530 if (p && (p = strchr(p, ',')))
536 if (!strchr(defs->variant, ',')) {
537 mdefs->variant[0] = defs->variant;
543 mdefs->variant[1] = Xstrdup(defs->variant);
544 if (mdefs->variant[1] == NULL)
546 squeeze_spaces(mdefs->variant[1]);
547 p = mdefs->variant[1];
548 for (i = 2; i <= XkbNumKbdGroups; i++) {
549 if ((p = strchr(p, ','))) {
551 mdefs->variant[i] = p;
557 if (p && (p = strchr(p, ',')))
565 FreeMultiDefs(XkbRF_MultiDefsPtr defs)
568 free(defs->layout[1]);
569 free(defs->variant[1]);
573 Apply(char *src, char **dst)
576 if (*src == '+' || *src == '!') {
577 *dst = _Concat(*dst, src);
587 XkbRF_ApplyRule(XkbRF_RulePtr rule, XkbComponentNamesPtr names)
589 rule->flags &= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
591 Apply(rule->keycodes, &names->keycodes);
592 Apply(rule->symbols, &names->symbols);
593 Apply(rule->types, &names->types);
594 Apply(rule->compat, &names->compat);
595 Apply(rule->geometry, &names->geometry);
599 CheckGroup(XkbRF_RulesPtr rules, char *group_name, char *name)
603 XkbRF_GroupPtr group;
605 for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
606 if (!strcmp(group->name, group_name)) {
610 if (i == rules->num_groups)
612 for (i = 0, p = group->words; i < group->number; i++, p += strlen(p) + 1) {
613 if (!strcmp(p, name)) {
621 XkbRF_CheckApplyRule(XkbRF_RulePtr rule,
622 XkbRF_MultiDefsPtr mdefs,
623 XkbComponentNamesPtr names, XkbRF_RulesPtr rules)
625 Bool pending = FALSE;
627 if (rule->model != NULL) {
628 if (mdefs->model == NULL)
630 if (strcmp(rule->model, "*") == 0) {
634 if (rule->model[0] == '$') {
635 if (!CheckGroup(rules, rule->model, mdefs->model))
639 if (strcmp(rule->model, mdefs->model) != 0)
644 if (rule->option != NULL) {
645 if (mdefs->options == NULL)
647 if ((!MatchOneOf(rule->option, mdefs->options)))
651 if (rule->layout != NULL) {
652 if (mdefs->layout[rule->layout_num] == NULL ||
653 *mdefs->layout[rule->layout_num] == '\0')
655 if (strcmp(rule->layout, "*") == 0) {
659 if (rule->layout[0] == '$') {
660 if (!CheckGroup(rules, rule->layout,
661 mdefs->layout[rule->layout_num]))
665 if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
670 if (rule->variant != NULL) {
671 if (mdefs->variant[rule->variant_num] == NULL ||
672 *mdefs->variant[rule->variant_num] == '\0')
674 if (strcmp(rule->variant, "*") == 0) {
678 if (rule->variant[0] == '$') {
679 if (!CheckGroup(rules, rule->variant,
680 mdefs->variant[rule->variant_num]))
684 if (strcmp(rule->variant,
685 mdefs->variant[rule->variant_num]) != 0)
691 rule->flags |= XkbRF_PendingMatch;
694 /* exact match, apply it now */
695 XkbRF_ApplyRule(rule, names);
700 XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
705 for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) {
706 rule->flags &= ~XkbRF_PendingMatch;
711 XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules, XkbComponentNamesPtr names)
716 for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
717 if ((rule->flags & XkbRF_PendingMatch) == 0)
719 XkbRF_ApplyRule(rule, names);
724 XkbRF_CheckApplyRules(XkbRF_RulesPtr rules,
725 XkbRF_MultiDefsPtr mdefs,
726 XkbComponentNamesPtr names, int flags)
732 for (rule = rules->rules, i = 0; i < rules->num_rules; rule++, i++) {
733 if ((rule->flags & flags) != flags)
735 skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
736 if (skip && !(flags & XkbRF_Option)) {
737 for (; (i < rules->num_rules) && (rule->number == skip);
745 /***====================================================================***/
748 XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
750 char *str, *outstr, *orig, *var;
754 str = index(name, '%');
758 while (str != NULL) {
762 if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) {
766 else if (pfx == '(') {
771 str = get_index(var + 1, &ndx);
773 str = index(str, '%');
776 if ((*var == 'l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
777 len += strlen(mdefs->layout[ndx]) + extra_len;
778 else if ((*var == 'm') && mdefs->model)
779 len += strlen(mdefs->model) + extra_len;
780 else if ((*var == 'v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
781 len += strlen(mdefs->variant[ndx]) + extra_len;
782 if ((pfx == '(') && (*str == ')')) {
785 str = index(&str[0], '%');
787 name = malloc(len + 1);
790 while (*str != '\0') {
797 if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) {
800 else if (pfx == '(') {
808 str = get_index(var + 1, &ndx);
812 if ((*var == 'l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
815 strcpy(outstr, mdefs->layout[ndx]);
816 outstr += strlen(mdefs->layout[ndx]);
820 else if ((*var == 'm') && (mdefs->model)) {
823 strcpy(outstr, mdefs->model);
824 outstr += strlen(mdefs->model);
828 else if ((*var == 'v') && mdefs->variant[ndx] &&
829 *mdefs->variant[ndx]) {
832 strcpy(outstr, mdefs->variant[ndx]);
833 outstr += strlen(mdefs->variant[ndx]);
837 if ((pfx == '(') && (*str == ')'))
850 /***====================================================================***/
853 XkbRF_GetComponents(XkbRF_RulesPtr rules,
854 XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names)
856 XkbRF_MultiDefsRec mdefs;
858 MakeMultiDefs(&mdefs, defs);
860 memset((char *) names, 0, sizeof(XkbComponentNamesRec));
861 XkbRF_ClearPartialMatches(rules);
862 XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
863 XkbRF_ApplyPartialMatches(rules, names);
864 XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
865 XkbRF_ApplyPartialMatches(rules, names);
866 XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
869 names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs);
871 names->symbols = XkbRF_SubstituteVars(names->symbols, &mdefs);
873 names->types = XkbRF_SubstituteVars(names->types, &mdefs);
875 names->compat = XkbRF_SubstituteVars(names->compat, &mdefs);
877 names->geometry = XkbRF_SubstituteVars(names->geometry, &mdefs);
879 FreeMultiDefs(&mdefs);
880 return (names->keycodes && names->symbols && names->types &&
881 names->compat && names->geometry);
885 XkbRF_AddRule(XkbRF_RulesPtr rules)
887 if (rules->sz_rules < 1) {
888 rules->sz_rules = 16;
889 rules->num_rules = 0;
890 rules->rules = calloc(rules->sz_rules, sizeof(XkbRF_RuleRec));
892 else if (rules->num_rules >= rules->sz_rules) {
893 rules->sz_rules *= 2;
894 rules->rules = realloc(rules->rules,
895 rules->sz_rules * sizeof(XkbRF_RuleRec));
898 rules->sz_rules = rules->num_rules = 0;
899 DebugF("Allocation failure in XkbRF_AddRule\n");
902 memset((char *) &rules->rules[rules->num_rules], 0, sizeof(XkbRF_RuleRec));
903 return &rules->rules[rules->num_rules++];
906 static XkbRF_GroupPtr
907 XkbRF_AddGroup(XkbRF_RulesPtr rules)
909 if (rules->sz_groups < 1) {
910 rules->sz_groups = 16;
911 rules->num_groups = 0;
912 rules->groups = calloc(rules->sz_groups, sizeof(XkbRF_GroupRec));
914 else if (rules->num_groups >= rules->sz_groups) {
915 rules->sz_groups *= 2;
916 rules->groups = realloc(rules->groups,
917 rules->sz_groups * sizeof(XkbRF_GroupRec));
919 if (!rules->groups) {
920 rules->sz_groups = rules->num_groups = 0;
924 memset((char *) &rules->groups[rules->num_groups], 0,
925 sizeof(XkbRF_GroupRec));
926 return &rules->groups[rules->num_groups++];
930 XkbRF_LoadRules(FILE * file, XkbRF_RulesPtr rules)
934 XkbRF_RuleRec trule, *rule;
935 XkbRF_GroupRec tgroup, *group;
937 if (!(rules && file))
939 memset((char *) &remap, 0, sizeof(RemapSpec));
940 memset((char *) &tgroup, 0, sizeof(XkbRF_GroupRec));
941 InitInputLine(&line);
942 while (GetInputLine(file, &line, TRUE)) {
943 if (CheckLine(&line, &remap, &trule, &tgroup)) {
945 if ((group = XkbRF_AddGroup(rules)) != NULL) {
947 memset((char *) &tgroup, 0, sizeof(XkbRF_GroupRec));
951 if ((rule = XkbRF_AddRule(rules)) != NULL) {
953 memset((char *) &trule, 0, sizeof(XkbRF_RuleRec));
959 FreeInputLine(&line);
964 XkbRF_LoadRulesByName(char *base, char *locale, XkbRF_RulesPtr rules)
970 if ((!base) || (!rules))
973 if (snprintf(buf, PATH_MAX, "%s-%s", base, locale) >= PATH_MAX)
977 if (strlen(base) + 1 > PATH_MAX)
982 file = fopen(buf, "r");
983 if ((!file) && (locale)) { /* fallback if locale was specified */
985 file = fopen(buf, "r");
989 ok = XkbRF_LoadRules(file, rules);
994 /***====================================================================***/
999 return calloc(1, sizeof(XkbRF_RulesRec));
1002 /***====================================================================***/
1005 XkbRF_Free(XkbRF_RulesPtr rules, Bool freeRules)
1009 XkbRF_GroupPtr group;
1014 for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) {
1017 free(rule->variant);
1019 free(rule->keycodes);
1020 free(rule->symbols);
1023 free(rule->geometry);
1024 memset((char *) rule, 0, sizeof(XkbRF_RuleRec));
1027 rules->num_rules = rules->sz_rules = 0;
1028 rules->rules = NULL;
1031 if (rules->groups) {
1032 for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
1036 free(rules->groups);
1037 rules->num_groups = 0;
1038 rules->groups = NULL;