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 const char *layout[XkbNumKbdGroups + 1];
223 const 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(const char *wanted, const char *vals_defined)
348 const char *str, *next;
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, const 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 options = Xstrdup(defs->options);
506 squeeze_spaces(options);
507 mdefs->options = options;
510 if (!strchr(defs->layout, ',')) {
511 mdefs->layout[0] = defs->layout;
518 layout = Xstrdup(defs->layout);
521 squeeze_spaces(layout);
522 mdefs->layout[1] = layout;
524 for (i = 2; i <= XkbNumKbdGroups; i++) {
525 if ((p = strchr(p, ','))) {
527 mdefs->layout[i] = p;
533 if (p && (p = strchr(p, ',')))
539 if (!strchr(defs->variant, ',')) {
540 mdefs->variant[0] = defs->variant;
547 variant = Xstrdup(defs->variant);
550 squeeze_spaces(variant);
551 mdefs->variant[1] = variant;
553 for (i = 2; i <= XkbNumKbdGroups; i++) {
554 if ((p = strchr(p, ','))) {
556 mdefs->variant[i] = p;
562 if (p && (p = strchr(p, ',')))
570 FreeMultiDefs(XkbRF_MultiDefsPtr defs)
572 free((void *) defs->options);
573 free((void *) defs->layout[1]);
574 free((void *) defs->variant[1]);
578 Apply(const char *src, char **dst)
581 if (*src == '+' || *src == '!') {
582 *dst = _Concat(*dst, src);
592 XkbRF_ApplyRule(XkbRF_RulePtr rule, XkbComponentNamesPtr names)
594 rule->flags &= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
596 Apply(rule->keycodes, &names->keycodes);
597 Apply(rule->symbols, &names->symbols);
598 Apply(rule->types, &names->types);
599 Apply(rule->compat, &names->compat);
600 Apply(rule->geometry, &names->geometry);
604 CheckGroup(XkbRF_RulesPtr rules, const char *group_name, const char *name)
608 XkbRF_GroupPtr group;
610 for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
611 if (!strcmp(group->name, group_name)) {
615 if (i == rules->num_groups)
617 for (i = 0, p = group->words; i < group->number; i++, p += strlen(p) + 1) {
618 if (!strcmp(p, name)) {
626 XkbRF_CheckApplyRule(XkbRF_RulePtr rule,
627 XkbRF_MultiDefsPtr mdefs,
628 XkbComponentNamesPtr names, XkbRF_RulesPtr rules)
630 Bool pending = FALSE;
632 if (rule->model != NULL) {
633 if (mdefs->model == NULL)
635 if (strcmp(rule->model, "*") == 0) {
639 if (rule->model[0] == '$') {
640 if (!CheckGroup(rules, rule->model, mdefs->model))
644 if (strcmp(rule->model, mdefs->model) != 0)
649 if (rule->option != NULL) {
650 if (mdefs->options == NULL)
652 if ((!MatchOneOf(rule->option, mdefs->options)))
656 if (rule->layout != NULL) {
657 if (mdefs->layout[rule->layout_num] == NULL ||
658 *mdefs->layout[rule->layout_num] == '\0')
660 if (strcmp(rule->layout, "*") == 0) {
664 if (rule->layout[0] == '$') {
665 if (!CheckGroup(rules, rule->layout,
666 mdefs->layout[rule->layout_num]))
670 if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
675 if (rule->variant != NULL) {
676 if (mdefs->variant[rule->variant_num] == NULL ||
677 *mdefs->variant[rule->variant_num] == '\0')
679 if (strcmp(rule->variant, "*") == 0) {
683 if (rule->variant[0] == '$') {
684 if (!CheckGroup(rules, rule->variant,
685 mdefs->variant[rule->variant_num]))
689 if (strcmp(rule->variant,
690 mdefs->variant[rule->variant_num]) != 0)
696 rule->flags |= XkbRF_PendingMatch;
699 /* exact match, apply it now */
700 XkbRF_ApplyRule(rule, names);
705 XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
710 for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) {
711 rule->flags &= ~XkbRF_PendingMatch;
716 XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules, XkbComponentNamesPtr names)
721 for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
722 if ((rule->flags & XkbRF_PendingMatch) == 0)
724 XkbRF_ApplyRule(rule, names);
729 XkbRF_CheckApplyRules(XkbRF_RulesPtr rules,
730 XkbRF_MultiDefsPtr mdefs,
731 XkbComponentNamesPtr names, int flags)
737 for (rule = rules->rules, i = 0; i < rules->num_rules; rule++, i++) {
738 if ((rule->flags & flags) != flags)
740 skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
741 if (skip && !(flags & XkbRF_Option)) {
742 for (; (i < rules->num_rules) && (rule->number == skip);
750 /***====================================================================***/
753 XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
755 char *str, *outstr, *orig, *var;
759 str = index(name, '%');
763 while (str != NULL) {
767 if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) {
771 else if (pfx == '(') {
776 str = get_index(var + 1, &ndx);
778 str = index(str, '%');
781 if ((*var == 'l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
782 len += strlen(mdefs->layout[ndx]) + extra_len;
783 else if ((*var == 'm') && mdefs->model)
784 len += strlen(mdefs->model) + extra_len;
785 else if ((*var == 'v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
786 len += strlen(mdefs->variant[ndx]) + extra_len;
787 if ((pfx == '(') && (*str == ')')) {
790 str = index(&str[0], '%');
792 name = malloc(len + 1);
795 while (*str != '\0') {
802 if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) {
805 else if (pfx == '(') {
813 str = get_index(var + 1, &ndx);
817 if ((*var == 'l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
820 strcpy(outstr, mdefs->layout[ndx]);
821 outstr += strlen(mdefs->layout[ndx]);
825 else if ((*var == 'm') && (mdefs->model)) {
828 strcpy(outstr, mdefs->model);
829 outstr += strlen(mdefs->model);
833 else if ((*var == 'v') && mdefs->variant[ndx] &&
834 *mdefs->variant[ndx]) {
837 strcpy(outstr, mdefs->variant[ndx]);
838 outstr += strlen(mdefs->variant[ndx]);
842 if ((pfx == '(') && (*str == ')'))
855 /***====================================================================***/
858 XkbRF_GetComponents(XkbRF_RulesPtr rules,
859 XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names)
861 XkbRF_MultiDefsRec mdefs;
863 MakeMultiDefs(&mdefs, defs);
865 memset((char *) names, 0, sizeof(XkbComponentNamesRec));
866 XkbRF_ClearPartialMatches(rules);
867 XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
868 XkbRF_ApplyPartialMatches(rules, names);
869 XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
870 XkbRF_ApplyPartialMatches(rules, names);
871 XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
872 XkbRF_ApplyPartialMatches(rules, names);
875 names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs);
877 names->symbols = XkbRF_SubstituteVars(names->symbols, &mdefs);
879 names->types = XkbRF_SubstituteVars(names->types, &mdefs);
881 names->compat = XkbRF_SubstituteVars(names->compat, &mdefs);
883 names->geometry = XkbRF_SubstituteVars(names->geometry, &mdefs);
885 FreeMultiDefs(&mdefs);
886 return (names->keycodes && names->symbols && names->types &&
887 names->compat && names->geometry);
891 XkbRF_AddRule(XkbRF_RulesPtr rules)
893 if (rules->sz_rules < 1) {
894 rules->sz_rules = 16;
895 rules->num_rules = 0;
896 rules->rules = calloc(rules->sz_rules, sizeof(XkbRF_RuleRec));
898 else if (rules->num_rules >= rules->sz_rules) {
899 rules->sz_rules *= 2;
900 rules->rules = realloc(rules->rules,
901 rules->sz_rules * sizeof(XkbRF_RuleRec));
904 rules->sz_rules = rules->num_rules = 0;
905 DebugF("Allocation failure in XkbRF_AddRule\n");
908 memset((char *) &rules->rules[rules->num_rules], 0, sizeof(XkbRF_RuleRec));
909 return &rules->rules[rules->num_rules++];
912 static XkbRF_GroupPtr
913 XkbRF_AddGroup(XkbRF_RulesPtr rules)
915 if (rules->sz_groups < 1) {
916 rules->sz_groups = 16;
917 rules->num_groups = 0;
918 rules->groups = calloc(rules->sz_groups, sizeof(XkbRF_GroupRec));
920 else if (rules->num_groups >= rules->sz_groups) {
921 rules->sz_groups *= 2;
922 rules->groups = realloc(rules->groups,
923 rules->sz_groups * sizeof(XkbRF_GroupRec));
925 if (!rules->groups) {
926 rules->sz_groups = rules->num_groups = 0;
930 memset((char *) &rules->groups[rules->num_groups], 0,
931 sizeof(XkbRF_GroupRec));
932 return &rules->groups[rules->num_groups++];
936 XkbRF_LoadRules(FILE * file, XkbRF_RulesPtr rules)
940 XkbRF_RuleRec trule, *rule;
941 XkbRF_GroupRec tgroup, *group;
943 if (!(rules && file))
945 memset((char *) &remap, 0, sizeof(RemapSpec));
946 memset((char *) &tgroup, 0, sizeof(XkbRF_GroupRec));
947 InitInputLine(&line);
948 while (GetInputLine(file, &line, TRUE)) {
949 if (CheckLine(&line, &remap, &trule, &tgroup)) {
951 if ((group = XkbRF_AddGroup(rules)) != NULL) {
953 memset((char *) &tgroup, 0, sizeof(XkbRF_GroupRec));
957 if ((rule = XkbRF_AddRule(rules)) != NULL) {
959 memset((char *) &trule, 0, sizeof(XkbRF_RuleRec));
965 FreeInputLine(&line);
970 XkbRF_LoadRulesByName(char *base, char *locale, XkbRF_RulesPtr rules)
976 if ((!base) || (!rules))
979 if (snprintf(buf, PATH_MAX, "%s-%s", base, locale) >= PATH_MAX)
983 if (strlen(base) + 1 > PATH_MAX)
988 file = fopen(buf, "r");
989 if ((!file) && (locale)) { /* fallback if locale was specified */
991 file = fopen(buf, "r");
995 ok = XkbRF_LoadRules(file, rules);
1000 /***====================================================================***/
1005 return calloc(1, sizeof(XkbRF_RulesRec));
1008 /***====================================================================***/
1011 XkbRF_Free(XkbRF_RulesPtr rules, Bool freeRules)
1015 XkbRF_GroupPtr group;
1020 for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) {
1021 free((void *) rule->model);
1022 free((void *) rule->layout);
1023 free((void *) rule->variant);
1024 free((void *) rule->option);
1025 free((void *) rule->keycodes);
1026 free((void *) rule->symbols);
1027 free((void *) rule->types);
1028 free((void *) rule->compat);
1029 free((void *) rule->geometry);
1030 memset((char *) rule, 0, sizeof(XkbRF_RuleRec));
1033 rules->num_rules = rules->sz_rules = 0;
1034 rules->rules = NULL;
1037 if (rules->groups) {
1038 for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
1039 free((void *) group->name);
1042 free(rules->groups);
1043 rules->num_groups = 0;
1044 rules->groups = NULL;