Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /************************************************************ |
2 | Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc. | |
3 | ||
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. | |
15 | ||
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. | |
24 | ||
25 | ********************************************************/ | |
26 | ||
27 | #ifdef HAVE_DIX_CONFIG_H | |
28 | #include <dix-config.h> | |
29 | #endif | |
30 | ||
31 | #include <stdio.h> | |
32 | #include <ctype.h> | |
33 | #include <stdlib.h> | |
34 | ||
35 | #define X_INCLUDE_STRING_H | |
36 | #define XOS_USE_NO_LOCKING | |
37 | #include <X11/Xos_r.h> | |
38 | ||
39 | #include <X11/Xproto.h> | |
40 | #include <X11/X.h> | |
41 | #include <X11/Xos.h> | |
42 | #include <X11/Xfuncs.h> | |
43 | #include <X11/Xatom.h> | |
44 | #include <X11/keysym.h> | |
45 | #include "misc.h" | |
46 | #include "inputstr.h" | |
47 | #include "dix.h" | |
48 | #include "os.h" | |
49 | #include "xkbstr.h" | |
50 | #define XKBSRV_NEED_FILE_FUNCS | |
51 | #include <xkbsrv.h> | |
52 | ||
53 | /***====================================================================***/ | |
54 | ||
55 | #define DFLT_LINE_SIZE 128 | |
56 | ||
57 | typedef struct { | |
58 | int line_num; | |
59 | int sz_line; | |
60 | int num_line; | |
61 | char buf[DFLT_LINE_SIZE]; | |
62 | char *line; | |
63 | } InputLine; | |
64 | ||
65 | static void | |
66 | InitInputLine(InputLine * line) | |
67 | { | |
68 | line->line_num = 1; | |
69 | line->num_line = 0; | |
70 | line->sz_line = DFLT_LINE_SIZE; | |
71 | line->line = line->buf; | |
72 | return; | |
73 | } | |
74 | ||
75 | static void | |
76 | FreeInputLine(InputLine * line) | |
77 | { | |
78 | if (line->line != line->buf) | |
79 | free(line->line); | |
80 | line->line_num = 1; | |
81 | line->num_line = 0; | |
82 | line->sz_line = DFLT_LINE_SIZE; | |
83 | line->line = line->buf; | |
84 | return; | |
85 | } | |
86 | ||
87 | static int | |
88 | InputLineAddChar(InputLine * line, int ch) | |
89 | { | |
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); | |
94 | } | |
95 | else { | |
96 | line->line = realloc((char *) line->line, line->sz_line * 2); | |
97 | } | |
98 | line->sz_line *= 2; | |
99 | } | |
100 | line->line[line->num_line++] = ch; | |
101 | return ch; | |
102 | } | |
103 | ||
104 | #define ADD_CHAR(l,c) ((l)->num_line<(l)->sz_line?\ | |
105 | (int)((l)->line[(l)->num_line++]= (c)):\ | |
106 | InputLineAddChar(l,c)) | |
107 | ||
108 | static Bool | |
109 | GetInputLine(FILE * file, InputLine * line, Bool checkbang) | |
110 | { | |
111 | int ch; | |
112 | Bool endOfFile, spacePending, slashPending, inComment; | |
113 | ||
114 | endOfFile = FALSE; | |
115 | while ((!endOfFile) && (line->num_line == 0)) { | |
116 | spacePending = slashPending = inComment = FALSE; | |
117 | while (((ch = getc(file)) != '\n') && (ch != EOF)) { | |
118 | if (ch == '\\') { | |
119 | if ((ch = getc(file)) == EOF) | |
120 | break; | |
121 | if (ch == '\n') { | |
122 | inComment = FALSE; | |
123 | ch = ' '; | |
124 | line->line_num++; | |
125 | } | |
126 | } | |
127 | if (inComment) | |
128 | continue; | |
129 | if (ch == '/') { | |
130 | if (slashPending) { | |
131 | inComment = TRUE; | |
132 | slashPending = FALSE; | |
133 | } | |
134 | else { | |
135 | slashPending = TRUE; | |
136 | } | |
137 | continue; | |
138 | } | |
139 | else if (slashPending) { | |
140 | if (spacePending) { | |
141 | ADD_CHAR(line, ' '); | |
142 | spacePending = FALSE; | |
143 | } | |
144 | ADD_CHAR(line, '/'); | |
145 | slashPending = FALSE; | |
146 | } | |
147 | if (isspace(ch)) { | |
148 | while (isspace(ch) && (ch != '\n') && (ch != EOF)) { | |
149 | ch = getc(file); | |
150 | } | |
151 | if (ch == EOF) | |
152 | break; | |
153 | if ((ch != '\n') && (line->num_line > 0)) | |
154 | spacePending = TRUE; | |
155 | ungetc(ch, file); | |
156 | } | |
157 | else { | |
158 | if (spacePending) { | |
159 | ADD_CHAR(line, ' '); | |
160 | spacePending = FALSE; | |
161 | } | |
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"); | |
166 | line->num_line = 0; | |
167 | inComment = 0; | |
168 | break; | |
169 | } | |
170 | ||
171 | } | |
172 | ADD_CHAR(line, ch); | |
173 | } | |
174 | } | |
175 | if (ch == EOF) | |
176 | endOfFile = TRUE; | |
177 | /* else line->num_line++;*/ | |
178 | } | |
179 | if ((line->num_line == 0) && (endOfFile)) | |
180 | return FALSE; | |
181 | ADD_CHAR(line, '\0'); | |
182 | return TRUE; | |
183 | } | |
184 | ||
185 | /***====================================================================***/ | |
186 | ||
187 | #define MODEL 0 | |
188 | #define LAYOUT 1 | |
189 | #define VARIANT 2 | |
190 | #define OPTION 3 | |
191 | #define KEYCODES 4 | |
192 | #define SYMBOLS 5 | |
193 | #define TYPES 6 | |
194 | #define COMPAT 7 | |
195 | #define GEOMETRY 8 | |
196 | #define MAX_WORDS 9 | |
197 | ||
198 | #define PART_MASK 0x000F | |
199 | #define COMPONENT_MASK 0x03F0 | |
200 | ||
201 | static const char *cname[MAX_WORDS] = { | |
202 | "model", "layout", "variant", "option", | |
203 | "keycodes", "symbols", "types", "compat", "geometry" | |
204 | }; | |
205 | ||
206 | typedef struct _RemapSpec { | |
207 | int number; | |
208 | int num_remap; | |
209 | struct { | |
210 | int word; | |
211 | int index; | |
212 | } remap[MAX_WORDS]; | |
213 | } RemapSpec; | |
214 | ||
215 | typedef struct _FileSpec { | |
216 | char *name[MAX_WORDS]; | |
217 | struct _FileSpec *pending; | |
218 | } FileSpec; | |
219 | ||
220 | typedef struct { | |
221 | char *model; | |
222 | char *layout[XkbNumKbdGroups + 1]; | |
223 | char *variant[XkbNumKbdGroups + 1]; | |
224 | char *options; | |
225 | } XkbRF_MultiDefsRec, *XkbRF_MultiDefsPtr; | |
226 | ||
227 | #define NDX_BUFF_SIZE 4 | |
228 | ||
229 | /***====================================================================***/ | |
230 | ||
231 | static char * | |
232 | get_index(char *str, int *ndx) | |
233 | { | |
234 | char ndx_buf[NDX_BUFF_SIZE]; | |
235 | char *end; | |
236 | ||
237 | if (*str != '[') { | |
238 | *ndx = 0; | |
239 | return str; | |
240 | } | |
241 | str++; | |
242 | end = strchr(str, ']'); | |
243 | if (end == NULL) { | |
244 | *ndx = -1; | |
245 | return str - 1; | |
246 | } | |
247 | if ((end - str) >= NDX_BUFF_SIZE) { | |
248 | *ndx = -1; | |
249 | return end + 1; | |
250 | } | |
251 | strlcpy(ndx_buf, str, 1 + end - str); | |
252 | *ndx = atoi(ndx_buf); | |
253 | return end + 1; | |
254 | } | |
255 | ||
256 | static void | |
257 | SetUpRemap(InputLine * line, RemapSpec * remap) | |
258 | { | |
259 | char *tok, *str; | |
260 | unsigned present, l_ndx_present, v_ndx_present; | |
261 | register int i; | |
262 | int len, ndx; | |
263 | _Xstrtokparams strtok_buf; | |
264 | Bool found; | |
265 | ||
266 | l_ndx_present = v_ndx_present = present = 0; | |
267 | str = &line->line[1]; | |
268 | len = remap->number; | |
269 | memset((char *) remap, 0, sizeof(RemapSpec)); | |
270 | remap->number = len; | |
271 | while ((tok = _XStrtok(str, " ", strtok_buf)) != NULL) { | |
272 | found = FALSE; | |
273 | str = NULL; | |
274 | if (strcmp(tok, "=") == 0) | |
275 | continue; | |
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); | |
281 | ||
282 | if ((i != LAYOUT && i != VARIANT) || | |
283 | *end != '\0' || ndx == -1) | |
284 | break; | |
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", | |
288 | XkbNumKbdGroups); | |
289 | break; | |
290 | } | |
291 | } | |
292 | else { | |
293 | ndx = 0; | |
294 | } | |
295 | found = TRUE; | |
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"); | |
301 | break; | |
302 | } | |
303 | } | |
304 | present |= (1 << i); | |
305 | if (i == LAYOUT) | |
306 | l_ndx_present |= 1 << ndx; | |
307 | if (i == VARIANT) | |
308 | v_ndx_present |= 1 << ndx; | |
309 | remap->remap[remap->num_remap].word = i; | |
310 | remap->remap[remap->num_remap++].index = ndx; | |
311 | break; | |
312 | } | |
313 | } | |
314 | if (!found) { | |
315 | fprintf(stderr, "Unknown component \"%s\" ignored\n", tok); | |
316 | } | |
317 | } | |
318 | if ((present & PART_MASK) == 0) { | |
319 | unsigned mask = PART_MASK; | |
320 | ||
321 | ErrorF("Mapping needs at least one of "); | |
322 | for (i = 0; (i < MAX_WORDS); i++) { | |
323 | if ((1L << i) & mask) { | |
324 | mask &= ~(1L << i); | |
325 | if (mask) | |
326 | DebugF("\"%s,\" ", cname[i]); | |
327 | else | |
328 | DebugF("or \"%s\"\n", cname[i]); | |
329 | } | |
330 | } | |
331 | DebugF("Illegal mapping ignored\n"); | |
332 | remap->num_remap = 0; | |
333 | return; | |
334 | } | |
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; | |
339 | return; | |
340 | } | |
341 | remap->number++; | |
342 | return; | |
343 | } | |
344 | ||
345 | static Bool | |
346 | MatchOneOf(char *wanted, char *vals_defined) | |
347 | { | |
348 | char *str, *next; | |
349 | int want_len = strlen(wanted); | |
350 | ||
351 | for (str = vals_defined, next = NULL; str != NULL; str = next) { | |
352 | int len; | |
353 | ||
354 | next = strchr(str, ','); | |
355 | if (next) { | |
356 | len = next - str; | |
357 | next++; | |
358 | } | |
359 | else { | |
360 | len = strlen(str); | |
361 | } | |
362 | if ((len == want_len) && (strncmp(wanted, str, len) == 0)) | |
363 | return TRUE; | |
364 | } | |
365 | return FALSE; | |
366 | } | |
367 | ||
368 | /***====================================================================***/ | |
369 | ||
370 | static Bool | |
371 | CheckLine(InputLine * line, | |
372 | RemapSpec * remap, XkbRF_RulePtr rule, XkbRF_GroupPtr group) | |
373 | { | |
374 | char *str, *tok; | |
375 | register int nread, i; | |
376 | FileSpec tmp; | |
377 | _Xstrtokparams strtok_buf; | |
378 | Bool append = FALSE; | |
379 | ||
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, ' '); | |
385 | ||
386 | if (!words) | |
387 | return FALSE; | |
388 | *words++ = '\0'; | |
389 | for (; *words; words++) { | |
390 | if (*words != '=' && *words != ' ') | |
391 | break; | |
392 | } | |
393 | if (*words == '\0') | |
394 | return FALSE; | |
395 | group->name = Xstrdup(gname); | |
396 | group->words = Xstrdup(words); | |
397 | for (i = 1, words = group->words; *words; words++) { | |
398 | if (*words == ' ') { | |
399 | *words++ = '\0'; | |
400 | i++; | |
401 | } | |
402 | } | |
403 | group->number = i; | |
404 | return TRUE; | |
405 | } | |
406 | else { | |
407 | SetUpRemap(line, remap); | |
408 | return FALSE; | |
409 | } | |
410 | } | |
411 | ||
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"); | |
415 | return FALSE; | |
416 | } | |
417 | memset((char *) &tmp, 0, sizeof(FileSpec)); | |
418 | str = line->line; | |
419 | for (nread = 0; (tok = _XStrtok(str, " ", strtok_buf)) != NULL; nread++) { | |
420 | str = NULL; | |
421 | if (strcmp(tok, "=") == 0) { | |
422 | nread--; | |
423 | continue; | |
424 | } | |
425 | if (nread > remap->num_remap) { | |
426 | DebugF("Too many words on a line\n"); | |
427 | DebugF("Extra word \"%s\" ignored\n", tok); | |
428 | continue; | |
429 | } | |
430 | tmp.name[remap->remap[nread].word] = tok; | |
431 | if (*tok == '+' || *tok == '|') | |
432 | append = TRUE; | |
433 | } | |
434 | if (nread < remap->num_remap) { | |
435 | DebugF("Too few words on a line: %s\n", line->line); | |
436 | DebugF("line ignored\n"); | |
437 | return FALSE; | |
438 | } | |
439 | ||
440 | rule->flags = 0; | |
441 | rule->number = remap->number; | |
442 | if (tmp.name[OPTION]) | |
443 | rule->flags |= XkbRF_Option; | |
444 | else if (append) | |
445 | rule->flags |= XkbRF_Append; | |
446 | else | |
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]); | |
452 | ||
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]); | |
458 | ||
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; | |
466 | } | |
467 | } | |
468 | return TRUE; | |
469 | } | |
470 | ||
471 | static char * | |
472 | _Concat(char *str1, char *str2) | |
473 | { | |
474 | int len; | |
475 | ||
476 | if ((!str1) || (!str2)) | |
477 | return str1; | |
478 | len = strlen(str1) + strlen(str2) + 1; | |
479 | str1 = realloc(str1, len * sizeof(char)); | |
480 | if (str1) | |
481 | strcat(str1, str2); | |
482 | return str1; | |
483 | } | |
484 | ||
485 | static void | |
486 | squeeze_spaces(char *p1) | |
487 | { | |
488 | char *p2; | |
489 | ||
490 | for (p2 = p1; *p2; p2++) { | |
491 | *p1 = *p2; | |
492 | if (*p1 != ' ') | |
493 | p1++; | |
494 | } | |
495 | *p1 = '\0'; | |
496 | } | |
497 | ||
498 | static Bool | |
499 | MakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs) | |
500 | { | |
501 | ||
502 | memset((char *) mdefs, 0, sizeof(XkbRF_MultiDefsRec)); | |
503 | mdefs->model = defs->model; | |
504 | mdefs->options = Xstrdup(defs->options); | |
505 | if (mdefs->options) | |
506 | squeeze_spaces(mdefs->options); | |
507 | ||
508 | if (defs->layout) { | |
509 | if (!strchr(defs->layout, ',')) { | |
510 | mdefs->layout[0] = defs->layout; | |
511 | } | |
512 | else { | |
513 | char *p; | |
514 | int i; | |
515 | ||
516 | mdefs->layout[1] = Xstrdup(defs->layout); | |
517 | if (mdefs->layout[1] == NULL) | |
518 | return FALSE; | |
519 | squeeze_spaces(mdefs->layout[1]); | |
520 | p = mdefs->layout[1]; | |
521 | for (i = 2; i <= XkbNumKbdGroups; i++) { | |
522 | if ((p = strchr(p, ','))) { | |
523 | *p++ = '\0'; | |
524 | mdefs->layout[i] = p; | |
525 | } | |
526 | else { | |
527 | break; | |
528 | } | |
529 | } | |
530 | if (p && (p = strchr(p, ','))) | |
531 | *p = '\0'; | |
532 | } | |
533 | } | |
534 | ||
535 | if (defs->variant) { | |
536 | if (!strchr(defs->variant, ',')) { | |
537 | mdefs->variant[0] = defs->variant; | |
538 | } | |
539 | else { | |
540 | char *p; | |
541 | int i; | |
542 | ||
543 | mdefs->variant[1] = Xstrdup(defs->variant); | |
544 | if (mdefs->variant[1] == NULL) | |
545 | return FALSE; | |
546 | squeeze_spaces(mdefs->variant[1]); | |
547 | p = mdefs->variant[1]; | |
548 | for (i = 2; i <= XkbNumKbdGroups; i++) { | |
549 | if ((p = strchr(p, ','))) { | |
550 | *p++ = '\0'; | |
551 | mdefs->variant[i] = p; | |
552 | } | |
553 | else { | |
554 | break; | |
555 | } | |
556 | } | |
557 | if (p && (p = strchr(p, ','))) | |
558 | *p = '\0'; | |
559 | } | |
560 | } | |
561 | return TRUE; | |
562 | } | |
563 | ||
564 | static void | |
565 | FreeMultiDefs(XkbRF_MultiDefsPtr defs) | |
566 | { | |
567 | free(defs->options); | |
568 | free(defs->layout[1]); | |
569 | free(defs->variant[1]); | |
570 | } | |
571 | ||
572 | static void | |
573 | Apply(char *src, char **dst) | |
574 | { | |
575 | if (src) { | |
576 | if (*src == '+' || *src == '!') { | |
577 | *dst = _Concat(*dst, src); | |
578 | } | |
579 | else { | |
580 | if (*dst == NULL) | |
581 | *dst = Xstrdup(src); | |
582 | } | |
583 | } | |
584 | } | |
585 | ||
586 | static void | |
587 | XkbRF_ApplyRule(XkbRF_RulePtr rule, XkbComponentNamesPtr names) | |
588 | { | |
589 | rule->flags &= ~XkbRF_PendingMatch; /* clear the flag because it's applied */ | |
590 | ||
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); | |
596 | } | |
597 | ||
598 | static Bool | |
599 | CheckGroup(XkbRF_RulesPtr rules, char *group_name, char *name) | |
600 | { | |
601 | int i; | |
602 | char *p; | |
603 | XkbRF_GroupPtr group; | |
604 | ||
605 | for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) { | |
606 | if (!strcmp(group->name, group_name)) { | |
607 | break; | |
608 | } | |
609 | } | |
610 | if (i == rules->num_groups) | |
611 | return FALSE; | |
612 | for (i = 0, p = group->words; i < group->number; i++, p += strlen(p) + 1) { | |
613 | if (!strcmp(p, name)) { | |
614 | return TRUE; | |
615 | } | |
616 | } | |
617 | return FALSE; | |
618 | } | |
619 | ||
620 | static int | |
621 | XkbRF_CheckApplyRule(XkbRF_RulePtr rule, | |
622 | XkbRF_MultiDefsPtr mdefs, | |
623 | XkbComponentNamesPtr names, XkbRF_RulesPtr rules) | |
624 | { | |
625 | Bool pending = FALSE; | |
626 | ||
627 | if (rule->model != NULL) { | |
628 | if (mdefs->model == NULL) | |
629 | return 0; | |
630 | if (strcmp(rule->model, "*") == 0) { | |
631 | pending = TRUE; | |
632 | } | |
633 | else { | |
634 | if (rule->model[0] == '$') { | |
635 | if (!CheckGroup(rules, rule->model, mdefs->model)) | |
636 | return 0; | |
637 | } | |
638 | else { | |
639 | if (strcmp(rule->model, mdefs->model) != 0) | |
640 | return 0; | |
641 | } | |
642 | } | |
643 | } | |
644 | if (rule->option != NULL) { | |
645 | if (mdefs->options == NULL) | |
646 | return 0; | |
647 | if ((!MatchOneOf(rule->option, mdefs->options))) | |
648 | return 0; | |
649 | } | |
650 | ||
651 | if (rule->layout != NULL) { | |
652 | if (mdefs->layout[rule->layout_num] == NULL || | |
653 | *mdefs->layout[rule->layout_num] == '\0') | |
654 | return 0; | |
655 | if (strcmp(rule->layout, "*") == 0) { | |
656 | pending = TRUE; | |
657 | } | |
658 | else { | |
659 | if (rule->layout[0] == '$') { | |
660 | if (!CheckGroup(rules, rule->layout, | |
661 | mdefs->layout[rule->layout_num])) | |
662 | return 0; | |
663 | } | |
664 | else { | |
665 | if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0) | |
666 | return 0; | |
667 | } | |
668 | } | |
669 | } | |
670 | if (rule->variant != NULL) { | |
671 | if (mdefs->variant[rule->variant_num] == NULL || | |
672 | *mdefs->variant[rule->variant_num] == '\0') | |
673 | return 0; | |
674 | if (strcmp(rule->variant, "*") == 0) { | |
675 | pending = TRUE; | |
676 | } | |
677 | else { | |
678 | if (rule->variant[0] == '$') { | |
679 | if (!CheckGroup(rules, rule->variant, | |
680 | mdefs->variant[rule->variant_num])) | |
681 | return 0; | |
682 | } | |
683 | else { | |
684 | if (strcmp(rule->variant, | |
685 | mdefs->variant[rule->variant_num]) != 0) | |
686 | return 0; | |
687 | } | |
688 | } | |
689 | } | |
690 | if (pending) { | |
691 | rule->flags |= XkbRF_PendingMatch; | |
692 | return rule->number; | |
693 | } | |
694 | /* exact match, apply it now */ | |
695 | XkbRF_ApplyRule(rule, names); | |
696 | return rule->number; | |
697 | } | |
698 | ||
699 | static void | |
700 | XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules) | |
701 | { | |
702 | register int i; | |
703 | XkbRF_RulePtr rule; | |
704 | ||
705 | for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) { | |
706 | rule->flags &= ~XkbRF_PendingMatch; | |
707 | } | |
708 | } | |
709 | ||
710 | static void | |
711 | XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules, XkbComponentNamesPtr names) | |
712 | { | |
713 | int i; | |
714 | XkbRF_RulePtr rule; | |
715 | ||
716 | for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) { | |
717 | if ((rule->flags & XkbRF_PendingMatch) == 0) | |
718 | continue; | |
719 | XkbRF_ApplyRule(rule, names); | |
720 | } | |
721 | } | |
722 | ||
723 | static void | |
724 | XkbRF_CheckApplyRules(XkbRF_RulesPtr rules, | |
725 | XkbRF_MultiDefsPtr mdefs, | |
726 | XkbComponentNamesPtr names, int flags) | |
727 | { | |
728 | int i; | |
729 | XkbRF_RulePtr rule; | |
730 | int skip; | |
731 | ||
732 | for (rule = rules->rules, i = 0; i < rules->num_rules; rule++, i++) { | |
733 | if ((rule->flags & flags) != flags) | |
734 | continue; | |
735 | skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules); | |
736 | if (skip && !(flags & XkbRF_Option)) { | |
737 | for (; (i < rules->num_rules) && (rule->number == skip); | |
738 | rule++, i++); | |
739 | rule--; | |
740 | i--; | |
741 | } | |
742 | } | |
743 | } | |
744 | ||
745 | /***====================================================================***/ | |
746 | ||
747 | static char * | |
748 | XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs) | |
749 | { | |
750 | char *str, *outstr, *orig, *var; | |
751 | int len, ndx; | |
752 | ||
753 | orig = name; | |
754 | str = index(name, '%'); | |
755 | if (str == NULL) | |
756 | return name; | |
757 | len = strlen(name); | |
758 | while (str != NULL) { | |
759 | char pfx = str[1]; | |
760 | int extra_len = 0; | |
761 | ||
762 | if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) { | |
763 | extra_len = 1; | |
764 | str++; | |
765 | } | |
766 | else if (pfx == '(') { | |
767 | extra_len = 2; | |
768 | str++; | |
769 | } | |
770 | var = str + 1; | |
771 | str = get_index(var + 1, &ndx); | |
772 | if (ndx == -1) { | |
773 | str = index(str, '%'); | |
774 | continue; | |
775 | } | |
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 == ')')) { | |
783 | str++; | |
784 | } | |
785 | str = index(&str[0], '%'); | |
786 | } | |
787 | name = malloc(len + 1); | |
788 | str = orig; | |
789 | outstr = name; | |
790 | while (*str != '\0') { | |
791 | if (str[0] == '%') { | |
792 | char pfx, sfx; | |
793 | ||
794 | str++; | |
795 | pfx = str[0]; | |
796 | sfx = '\0'; | |
797 | if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) { | |
798 | str++; | |
799 | } | |
800 | else if (pfx == '(') { | |
801 | sfx = ')'; | |
802 | str++; | |
803 | } | |
804 | else | |
805 | pfx = '\0'; | |
806 | ||
807 | var = str; | |
808 | str = get_index(var + 1, &ndx); | |
809 | if (ndx == -1) { | |
810 | continue; | |
811 | } | |
812 | if ((*var == 'l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) { | |
813 | if (pfx) | |
814 | *outstr++ = pfx; | |
815 | strcpy(outstr, mdefs->layout[ndx]); | |
816 | outstr += strlen(mdefs->layout[ndx]); | |
817 | if (sfx) | |
818 | *outstr++ = sfx; | |
819 | } | |
820 | else if ((*var == 'm') && (mdefs->model)) { | |
821 | if (pfx) | |
822 | *outstr++ = pfx; | |
823 | strcpy(outstr, mdefs->model); | |
824 | outstr += strlen(mdefs->model); | |
825 | if (sfx) | |
826 | *outstr++ = sfx; | |
827 | } | |
828 | else if ((*var == 'v') && mdefs->variant[ndx] && | |
829 | *mdefs->variant[ndx]) { | |
830 | if (pfx) | |
831 | *outstr++ = pfx; | |
832 | strcpy(outstr, mdefs->variant[ndx]); | |
833 | outstr += strlen(mdefs->variant[ndx]); | |
834 | if (sfx) | |
835 | *outstr++ = sfx; | |
836 | } | |
837 | if ((pfx == '(') && (*str == ')')) | |
838 | str++; | |
839 | } | |
840 | else { | |
841 | *outstr++ = *str++; | |
842 | } | |
843 | } | |
844 | *outstr++ = '\0'; | |
845 | if (orig != name) | |
846 | free(orig); | |
847 | return name; | |
848 | } | |
849 | ||
850 | /***====================================================================***/ | |
851 | ||
852 | Bool | |
853 | XkbRF_GetComponents(XkbRF_RulesPtr rules, | |
854 | XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names) | |
855 | { | |
856 | XkbRF_MultiDefsRec mdefs; | |
857 | ||
858 | MakeMultiDefs(&mdefs, defs); | |
859 | ||
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); | |
867 | XkbRF_ApplyPartialMatches(rules, names); | |
868 | ||
869 | if (names->keycodes) | |
870 | names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs); | |
871 | if (names->symbols) | |
872 | names->symbols = XkbRF_SubstituteVars(names->symbols, &mdefs); | |
873 | if (names->types) | |
874 | names->types = XkbRF_SubstituteVars(names->types, &mdefs); | |
875 | if (names->compat) | |
876 | names->compat = XkbRF_SubstituteVars(names->compat, &mdefs); | |
877 | if (names->geometry) | |
878 | names->geometry = XkbRF_SubstituteVars(names->geometry, &mdefs); | |
879 | ||
880 | FreeMultiDefs(&mdefs); | |
881 | return (names->keycodes && names->symbols && names->types && | |
882 | names->compat && names->geometry); | |
883 | } | |
884 | ||
885 | static XkbRF_RulePtr | |
886 | XkbRF_AddRule(XkbRF_RulesPtr rules) | |
887 | { | |
888 | if (rules->sz_rules < 1) { | |
889 | rules->sz_rules = 16; | |
890 | rules->num_rules = 0; | |
891 | rules->rules = calloc(rules->sz_rules, sizeof(XkbRF_RuleRec)); | |
892 | } | |
893 | else if (rules->num_rules >= rules->sz_rules) { | |
894 | rules->sz_rules *= 2; | |
895 | rules->rules = realloc(rules->rules, | |
896 | rules->sz_rules * sizeof(XkbRF_RuleRec)); | |
897 | } | |
898 | if (!rules->rules) { | |
899 | rules->sz_rules = rules->num_rules = 0; | |
900 | DebugF("Allocation failure in XkbRF_AddRule\n"); | |
901 | return NULL; | |
902 | } | |
903 | memset((char *) &rules->rules[rules->num_rules], 0, sizeof(XkbRF_RuleRec)); | |
904 | return &rules->rules[rules->num_rules++]; | |
905 | } | |
906 | ||
907 | static XkbRF_GroupPtr | |
908 | XkbRF_AddGroup(XkbRF_RulesPtr rules) | |
909 | { | |
910 | if (rules->sz_groups < 1) { | |
911 | rules->sz_groups = 16; | |
912 | rules->num_groups = 0; | |
913 | rules->groups = calloc(rules->sz_groups, sizeof(XkbRF_GroupRec)); | |
914 | } | |
915 | else if (rules->num_groups >= rules->sz_groups) { | |
916 | rules->sz_groups *= 2; | |
917 | rules->groups = realloc(rules->groups, | |
918 | rules->sz_groups * sizeof(XkbRF_GroupRec)); | |
919 | } | |
920 | if (!rules->groups) { | |
921 | rules->sz_groups = rules->num_groups = 0; | |
922 | return NULL; | |
923 | } | |
924 | ||
925 | memset((char *) &rules->groups[rules->num_groups], 0, | |
926 | sizeof(XkbRF_GroupRec)); | |
927 | return &rules->groups[rules->num_groups++]; | |
928 | } | |
929 | ||
930 | Bool | |
931 | XkbRF_LoadRules(FILE * file, XkbRF_RulesPtr rules) | |
932 | { | |
933 | InputLine line; | |
934 | RemapSpec remap; | |
935 | XkbRF_RuleRec trule, *rule; | |
936 | XkbRF_GroupRec tgroup, *group; | |
937 | ||
938 | if (!(rules && file)) | |
939 | return FALSE; | |
940 | memset((char *) &remap, 0, sizeof(RemapSpec)); | |
941 | memset((char *) &tgroup, 0, sizeof(XkbRF_GroupRec)); | |
942 | InitInputLine(&line); | |
943 | while (GetInputLine(file, &line, TRUE)) { | |
944 | if (CheckLine(&line, &remap, &trule, &tgroup)) { | |
945 | if (tgroup.number) { | |
946 | if ((group = XkbRF_AddGroup(rules)) != NULL) { | |
947 | *group = tgroup; | |
948 | memset((char *) &tgroup, 0, sizeof(XkbRF_GroupRec)); | |
949 | } | |
950 | } | |
951 | else { | |
952 | if ((rule = XkbRF_AddRule(rules)) != NULL) { | |
953 | *rule = trule; | |
954 | memset((char *) &trule, 0, sizeof(XkbRF_RuleRec)); | |
955 | } | |
956 | } | |
957 | } | |
958 | line.num_line = 0; | |
959 | } | |
960 | FreeInputLine(&line); | |
961 | return TRUE; | |
962 | } | |
963 | ||
964 | Bool | |
965 | XkbRF_LoadRulesByName(char *base, char *locale, XkbRF_RulesPtr rules) | |
966 | { | |
967 | FILE *file; | |
968 | char buf[PATH_MAX]; | |
969 | Bool ok; | |
970 | ||
971 | if ((!base) || (!rules)) | |
972 | return FALSE; | |
973 | if (locale) { | |
974 | if (snprintf(buf, PATH_MAX, "%s-%s", base, locale) >= PATH_MAX) | |
975 | return FALSE; | |
976 | } | |
977 | else { | |
978 | if (strlen(base) + 1 > PATH_MAX) | |
979 | return FALSE; | |
980 | strcpy(buf, base); | |
981 | } | |
982 | ||
983 | file = fopen(buf, "r"); | |
984 | if ((!file) && (locale)) { /* fallback if locale was specified */ | |
985 | strcpy(buf, base); | |
986 | file = fopen(buf, "r"); | |
987 | } | |
988 | if (!file) | |
989 | return FALSE; | |
990 | ok = XkbRF_LoadRules(file, rules); | |
991 | fclose(file); | |
992 | return ok; | |
993 | } | |
994 | ||
995 | /***====================================================================***/ | |
996 | ||
997 | XkbRF_RulesPtr | |
998 | XkbRF_Create(void) | |
999 | { | |
1000 | return calloc(1, sizeof(XkbRF_RulesRec)); | |
1001 | } | |
1002 | ||
1003 | /***====================================================================***/ | |
1004 | ||
1005 | void | |
1006 | XkbRF_Free(XkbRF_RulesPtr rules, Bool freeRules) | |
1007 | { | |
1008 | int i; | |
1009 | XkbRF_RulePtr rule; | |
1010 | XkbRF_GroupPtr group; | |
1011 | ||
1012 | if (!rules) | |
1013 | return; | |
1014 | if (rules->rules) { | |
1015 | for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) { | |
1016 | free(rule->model); | |
1017 | free(rule->layout); | |
1018 | free(rule->variant); | |
1019 | free(rule->option); | |
1020 | free(rule->keycodes); | |
1021 | free(rule->symbols); | |
1022 | free(rule->types); | |
1023 | free(rule->compat); | |
1024 | free(rule->geometry); | |
1025 | memset((char *) rule, 0, sizeof(XkbRF_RuleRec)); | |
1026 | } | |
1027 | free(rules->rules); | |
1028 | rules->num_rules = rules->sz_rules = 0; | |
1029 | rules->rules = NULL; | |
1030 | } | |
1031 | ||
1032 | if (rules->groups) { | |
1033 | for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) { | |
1034 | free(group->name); | |
1035 | free(group->words); | |
1036 | } | |
1037 | free(rules->groups); | |
1038 | rules->num_groups = 0; | |
1039 | rules->groups = NULL; | |
1040 | } | |
1041 | if (freeRules) | |
1042 | free(rules); | |
1043 | return; | |
1044 | } |