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 /***====================================================================***/
185 #define PART_MASK 0x000F
186 #define COMPONENT_MASK 0x03F0
188 static const char * cname[MAX_WORDS] = {
189 "model", "layout", "variant", "option",
190 "keycodes", "symbols", "types", "compat", "geometry", "keymap"
210 char *name[MAX_WORDS];
211 struct file_spec *pending;
216 const char *layout[XkbNumKbdGroups + 1];
217 const char *variant[XkbNumKbdGroups + 1];
226 struct describe_vars {
229 struct var_desc *desc;
238 #define XkbRF_PendingMatch (1L<<1)
239 #define XkbRF_Option (1L<<2)
240 #define XkbRF_Append (1L<<3)
241 #define XkbRF_Normal (1L<<4)
242 #define XkbRF_Invalid (1L<<5)
264 struct describe_vars models;
265 struct describe_vars layouts;
266 struct describe_vars variants;
267 struct describe_vars options;
275 struct group *groups;
278 #define NDX_BUFF_SIZE 4
280 /***====================================================================***/
283 get_index(char *str, int *ndx)
285 char ndx_buf[NDX_BUFF_SIZE];
293 end = strchr(str, ']');
298 if ( (end - str) >= NDX_BUFF_SIZE) {
302 strncpy(ndx_buf, str, end - str);
303 ndx_buf[end - str] = '\0';
304 *ndx = atoi(ndx_buf);
309 SetUpRemap(struct input_line *line, struct remap_spec *remap)
312 unsigned present, l_ndx_present, v_ndx_present;
319 l_ndx_present = v_ndx_present = present= 0;
322 memset(remap, 0, sizeof(*remap));
324 while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
327 if (strcmp(tok,"=")==0)
329 for (i=0;i<MAX_WORDS;i++) {
330 len = strlen(cname[i]);
331 if (strncmp(cname[i],tok,len)==0) {
332 if(strlen(tok) > len) {
333 char *end = get_index(tok+len, &ndx);
334 if ((i != LAYOUT && i != VARIANT) ||
335 *end != '\0' || ndx == -1)
337 if (ndx < 1 || ndx > XkbNumKbdGroups) {
338 WARN("Illegal %s index: %d\n", cname[i], ndx);
339 WARN("Index must be in range 1..%d\n", XkbNumKbdGroups);
348 if (present&(1<<i)) {
349 if ((i == LAYOUT && l_ndx_present&(1<<ndx)) ||
350 (i == VARIANT && v_ndx_present&(1<<ndx)) ) {
351 WARN("Component \"%s\" listed twice\n", tok);
352 ACTION("Second definition ignored\n");
358 l_ndx_present |= 1 << ndx;
360 v_ndx_present |= 1 << ndx;
361 remap->remap[remap->num_remap].word= i;
362 remap->remap[remap->num_remap++].index= ndx;
368 WARN("Unknown component \"%s\"\n", tok);
372 if ((present&PART_MASK)==0) {
373 unsigned mask= PART_MASK;
375 /* FIXME: Use log function instead of fprintf. */
376 WARN("Mapping needs at least one of ");
377 for (i=0; (i<MAX_WORDS); i++) {
380 if (mask) fprintf(stderr,"\"%s,\" ",cname[i]);
381 else fprintf(stderr,"or \"%s\"\n",cname[i]);
384 ACTION("Illegal mapping ignored\n");
389 if ((present&COMPONENT_MASK)==0) {
390 WARN("Mapping needs at least one component\n");
391 ACTION("Illegal mapping ignored\n");
395 if (((present&COMPONENT_MASK)&(1<<KEYMAP))&&
396 ((present&COMPONENT_MASK)!=(1<<KEYMAP))) {
397 WARN("Keymap cannot appear with other components\n");
398 ACTION("Illegal mapping ignored\n");
406 MatchOneOf(char *wanted,char *vals_defined)
409 int want_len = strlen(wanted);
411 for (str=vals_defined,next=NULL;str!=NULL;str=next) {
413 next= strchr(str,',');
421 if ((len==want_len)&&(strncmp(wanted,str,len)==0))
427 /***====================================================================***/
430 CheckLine(struct input_line *line, struct remap_spec *remap,
431 struct rule *rule, struct group *group)
435 struct file_spec tmp;
439 if (line->line[0]=='!') {
440 if (line->line[1] == '$' ||
441 (line->line[1] == ' ' && line->line[2] == '$')) {
442 char *gname = strchr(line->line, '$');
443 char *words = strchr(gname, ' ');
447 for (; *words; words++) {
448 if (*words != '=' && *words != ' ')
453 group->name = uDupString(gname);
454 group->words = uDupString(words);
455 for (i = 1, words = group->words; *words; words++) {
456 if ( *words == ' ') {
464 SetUpRemap(line,remap);
469 if (remap->num_remap==0) {
470 WARN("Must have a mapping before first line of data\n");
471 ACTION("Illegal line of data ignored\n");
474 memset(&tmp, 0, sizeof(tmp));
476 for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL; nread++) {
478 if (strcmp(tok,"=")==0) {
482 if (nread>remap->num_remap) {
483 WARN("Too many words on a line\n");
484 ACTION("Extra word \"%s\" ignored\n",tok);
487 tmp.name[remap->remap[nread].word]= tok;
488 if (*tok == '+' || *tok == '|')
491 if (nread<remap->num_remap) {
492 WARN("Too few words on a line: %s\n", line->line);
493 ACTION("line ignored\n");
498 rule->number = remap->number;
499 if (tmp.name[OPTION])
500 rule->flags|= XkbRF_Option;
502 rule->flags|= XkbRF_Append;
504 rule->flags|= XkbRF_Normal;
505 rule->model= uDupString(tmp.name[MODEL]);
506 rule->layout= uDupString(tmp.name[LAYOUT]);
507 rule->variant= uDupString(tmp.name[VARIANT]);
508 rule->option= uDupString(tmp.name[OPTION]);
510 rule->keycodes= uDupString(tmp.name[KEYCODES]);
511 rule->symbols= uDupString(tmp.name[SYMBOLS]);
512 rule->types= uDupString(tmp.name[TYPES]);
513 rule->compat= uDupString(tmp.name[COMPAT]);
514 rule->keymap= uDupString(tmp.name[KEYMAP]);
516 rule->layout_num = rule->variant_num = 0;
517 for (i = 0; i < nread; i++) {
518 if (remap->remap[i].index) {
519 if (remap->remap[i].word == LAYOUT)
520 rule->layout_num = remap->remap[i].index;
521 if (remap->remap[i].word == VARIANT)
522 rule->variant_num = remap->remap[i].index;
529 _Concat(char *str1,char *str2)
533 if ((!str1)||(!str2))
535 len= strlen(str1)+strlen(str2)+1;
536 str1 = uTypedRealloc(str1, len, char);
543 squeeze_spaces(char *p1)
546 for (p2 = p1; *p2; p2++) {
548 if (*p1 != ' ') p1++;
554 MakeMultiDefs(struct multi_defs *mdefs, const struct var_defs *defs)
556 memset(mdefs, 0, sizeof(*mdefs));
557 mdefs->model = defs->model;
558 mdefs->options = uDupString(defs->options);
559 if (mdefs->options) squeeze_spaces(mdefs->options);
562 if (!strchr(defs->layout, ',')) {
563 mdefs->layout[0] = defs->layout;
567 p = uDupString(defs->layout);
571 mdefs->layout[1] = p;
572 for (i = 2; i <= XkbNumKbdGroups; i++) {
573 if ((p = strchr(p, ','))) {
575 mdefs->layout[i] = p;
580 if (p && (p = strchr(p, ',')))
586 if (!strchr(defs->variant, ',')) {
587 mdefs->variant[0] = defs->variant;
591 p = uDupString(defs->variant);
595 mdefs->variant[1] = p;
596 for (i = 2; i <= XkbNumKbdGroups; i++) {
597 if ((p = strchr(p, ','))) {
599 mdefs->variant[i] = p;
604 if (p && (p = strchr(p, ',')))
612 FreeMultiDefs(struct multi_defs *defs)
615 free(UNCONSTIFY(defs->layout[1]));
616 free(UNCONSTIFY(defs->variant[1]));
620 Apply(char *src, char **dst)
623 if (*src == '+' || *src == '!') {
624 *dst= _Concat(*dst, src);
627 *dst= uDupString(src);
633 XkbRF_ApplyRule(struct rule *rule, struct xkb_component_names *names)
635 rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
637 Apply(rule->keycodes, &names->keycodes);
638 Apply(rule->symbols, &names->symbols);
639 Apply(rule->types, &names->types);
640 Apply(rule->compat, &names->compat);
641 Apply(rule->keymap, &names->keymap);
645 CheckGroup(struct rules *rules, const char *group_name, const char *name)
651 for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
652 if (! strcmp(group->name, group_name)) {
656 if (i == rules->num_groups)
658 for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
659 if (! strcmp(p, name)) {
667 XkbRF_CheckApplyRule(struct rule *rule, struct multi_defs *mdefs,
668 struct xkb_component_names *names, struct rules *rules)
670 bool pending = false;
672 if (rule->model != NULL) {
673 if(mdefs->model == NULL)
675 if (strcmp(rule->model, "*") == 0) {
678 if (rule->model[0] == '$') {
679 if (!CheckGroup(rules, rule->model, mdefs->model))
682 if (strcmp(rule->model, mdefs->model) != 0)
687 if (rule->option != NULL) {
688 if (mdefs->options == NULL)
690 if ((!MatchOneOf(rule->option,mdefs->options)))
694 if (rule->layout != NULL) {
695 if(mdefs->layout[rule->layout_num] == NULL ||
696 *mdefs->layout[rule->layout_num] == '\0')
698 if (strcmp(rule->layout, "*") == 0) {
701 if (rule->layout[0] == '$') {
702 if (!CheckGroup(rules, rule->layout,
703 mdefs->layout[rule->layout_num]))
706 if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
711 if (rule->variant != NULL) {
712 if (mdefs->variant[rule->variant_num] == NULL ||
713 *mdefs->variant[rule->variant_num] == '\0')
715 if (strcmp(rule->variant, "*") == 0) {
718 if (rule->variant[0] == '$') {
719 if (!CheckGroup(rules, rule->variant,
720 mdefs->variant[rule->variant_num]))
723 if (strcmp(rule->variant,
724 mdefs->variant[rule->variant_num]) != 0)
730 rule->flags|= XkbRF_PendingMatch;
733 /* exact match, apply it now */
734 XkbRF_ApplyRule(rule,names);
739 XkbRF_ClearPartialMatches(struct rules *rules)
744 for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
745 rule->flags&= ~XkbRF_PendingMatch;
750 XkbRF_ApplyPartialMatches(struct rules *rules,
751 struct xkb_component_names *names)
756 for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
757 if ((rule->flags&XkbRF_PendingMatch)==0)
759 XkbRF_ApplyRule(rule,names);
764 XkbRF_CheckApplyRules(struct rules *rules, struct multi_defs *mdefs,
765 struct xkb_component_names *names, unsigned int flags)
771 for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
772 if ((rule->flags & flags) != flags)
774 skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
775 if (skip && !(flags & XkbRF_Option)) {
776 for ( ;(i < rules->num_rules) && (rule->number == skip);
783 /***====================================================================***/
786 XkbRF_SubstituteVars(char *name, struct multi_defs *mdefs)
788 char *str, *outstr, *orig, *var;
796 str= strchr(name,'%');
803 if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
812 str = get_index(var + 1, &ndx);
814 str = strchr(str,'%');
817 if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
818 len+= strlen(mdefs->layout[ndx])+extra_len;
819 else if ((*var=='m')&&mdefs->model)
820 len+= strlen(mdefs->model)+extra_len;
821 else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
822 len+= strlen(mdefs->variant[ndx])+extra_len;
823 if ((pfx=='(')&&(*str==')')) {
826 str= strchr(&str[0],'%');
828 name = malloc(len + 1);
837 if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
847 str = get_index(var + 1, &ndx);
851 if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
852 if (pfx) *outstr++= pfx;
853 strcpy(outstr,mdefs->layout[ndx]);
854 outstr+= strlen(mdefs->layout[ndx]);
855 if (sfx) *outstr++= sfx;
857 else if ((*var=='m')&&(mdefs->model)) {
858 if (pfx) *outstr++= pfx;
859 strcpy(outstr,mdefs->model);
860 outstr+= strlen(mdefs->model);
861 if (sfx) *outstr++= sfx;
863 else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
864 if (pfx) *outstr++= pfx;
865 strcpy(outstr,mdefs->variant[ndx]);
866 outstr+= strlen(mdefs->variant[ndx]);
867 if (sfx) *outstr++= sfx;
869 if ((pfx=='(')&&(*str==')'))
882 /***====================================================================***/
885 XkbcRF_GetComponents(struct rules *rules, const struct var_defs *defs,
886 struct xkb_component_names *names)
888 struct multi_defs mdefs;
890 MakeMultiDefs(&mdefs, defs);
892 memset(names, 0, sizeof(struct xkb_component_names));
893 XkbRF_ClearPartialMatches(rules);
894 XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
895 XkbRF_ApplyPartialMatches(rules, names);
896 XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
897 XkbRF_ApplyPartialMatches(rules, names);
898 XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
899 XkbRF_ApplyPartialMatches(rules, names);
901 names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs);
902 names->symbols = XkbRF_SubstituteVars(names->symbols, &mdefs);
903 names->types = XkbRF_SubstituteVars(names->types, &mdefs);
904 names->compat = XkbRF_SubstituteVars(names->compat, &mdefs);
905 names->keymap = XkbRF_SubstituteVars(names->keymap, &mdefs);
907 FreeMultiDefs(&mdefs);
908 return (names->keycodes && names->symbols && names->types &&
909 names->compat) || names->keymap;
913 XkbcRF_AddRule(struct rules *rules)
915 if (rules->sz_rules<1) {
918 rules->rules = calloc(rules->sz_rules, sizeof(*rules->rules));
920 else if (rules->num_rules>=rules->sz_rules) {
922 rules->rules = realloc(rules->rules,
923 rules->sz_rules * sizeof(*rules->rules));
926 rules->sz_rules= rules->num_rules= 0;
929 memset(&rules->rules[rules->num_rules], 0, sizeof(*rules->rules));
930 return &rules->rules[rules->num_rules++];
933 static struct group *
934 XkbcRF_AddGroup(struct rules *rules)
936 if (rules->sz_groups<1) {
937 rules->sz_groups= 16;
938 rules->num_groups= 0;
939 rules->groups = calloc(rules->sz_groups, sizeof(*rules->groups));
941 else if (rules->num_groups >= rules->sz_groups) {
942 rules->sz_groups *= 2;
943 rules->groups = realloc(rules->groups,
944 rules->sz_groups * sizeof(*rules->groups));
946 if (!rules->groups) {
947 rules->sz_groups= rules->num_groups= 0;
951 memset(&rules->groups[rules->num_groups], 0, sizeof(*rules->groups));
952 return &rules->groups[rules->num_groups++];
955 static struct rules *
956 XkbcRF_LoadRules(FILE *file)
958 struct input_line line;
959 struct remap_spec remap;
960 struct rule trule, *rule;
961 struct group tgroup, *group;
964 rules = calloc(1, sizeof(*rules));
968 memset(&remap, 0, sizeof(remap));
969 memset(&tgroup, 0, sizeof(tgroup));
970 input_line_init(&line);
972 while (input_line_get(file, &line)) {
973 if (CheckLine(&line,&remap,&trule,&tgroup)) {
975 if ((group= XkbcRF_AddGroup(rules))!=NULL) {
977 memset(&tgroup, 0, sizeof(tgroup));
980 if ((rule= XkbcRF_AddRule(rules))!=NULL) {
982 memset(&trule, 0, sizeof(trule));
988 input_line_deinit(&line);
993 XkbRF_ClearVarDescriptions(struct describe_vars *var)
997 for (i=0;i<var->num_desc;i++) {
998 free(var->desc[i].name);
999 free(var->desc[i].desc);
1000 var->desc[i].name= var->desc[i].desc= NULL;
1007 XkbcRF_Free(struct rules *rules)
1011 struct group *group;
1015 XkbRF_ClearVarDescriptions(&rules->models);
1016 XkbRF_ClearVarDescriptions(&rules->layouts);
1017 XkbRF_ClearVarDescriptions(&rules->variants);
1018 XkbRF_ClearVarDescriptions(&rules->options);
1020 for (i=0, rule = rules->rules; i < rules->num_rules && rules; i++, rule++) {
1023 free(rule->variant);
1025 free(rule->keycodes);
1026 free(rule->symbols);
1033 for (i=0, group = rules->groups; i < rules->num_groups && group; i++, group++) {
1037 free(rules->groups);
1042 struct xkb_component_names *
1043 xkb_components_from_rules(struct xkb_context *ctx,
1044 const struct xkb_rule_names *rmlvo)
1049 struct rules *loaded;
1050 struct xkb_component_names *names = NULL;
1051 struct var_defs defs = {
1052 .model = rmlvo->model,
1053 .layout = rmlvo->layout,
1054 .variant = rmlvo->variant,
1055 .options = rmlvo->options,
1058 rulesFile = XkbFindFileInPath(ctx, rmlvo->rules, XkmRulesFile,
1061 ERROR("could not find \"%s\" rules in XKB path\n", rmlvo->rules);
1062 ERROR("%d include paths searched:\n",
1063 xkb_context_num_include_paths(ctx));
1064 for (i = 0; i < xkb_context_num_include_paths(ctx); i++)
1065 ERROR("\t%s\n", xkb_context_include_path_get(ctx, i));
1069 loaded = XkbcRF_LoadRules(rulesFile);
1071 ERROR("failed to load XKB rules \"%s\"\n", rulesPath);
1075 names = calloc(1, sizeof(*names));
1077 ERROR("failed to allocate XKB components\n");
1081 if (!XkbcRF_GetComponents(loaded, &defs, names)) {
1082 free(names->keymap);
1083 free(names->keycodes);
1085 free(names->compat);
1086 free(names->symbols);
1089 ERROR("no components returned from XKB rules \"%s\"\n", rulesPath);
1093 XkbcRF_Free(loaded);