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 /* 28 * Copyright 2012 Ran Benita <ran234 (at) gmail.com> 29 * 30 * Permission is hereby granted, free of charge, to any person obtaining a 31 * copy of this software and associated documentation files (the "Software"), 32 * to deal in the Software without restriction, including without limitation 33 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 34 * and/or sell copies of the Software, and to permit persons to whom the 35 * Software is furnished to do so, subject to the following conditions: 36 * 37 * The above copyright notice and this permission notice (including the next 38 * paragraph) shall be included in all copies or substantial portions of the 39 * Software. 40 * 41 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 44 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 46 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 47 * DEALINGS IN THE SOFTWARE. 48 */ 49 50 #include "xkbcomp-priv.h" 51 #include "rules.h" 52 #include "include.h" 53 #include "scanner-utils.h" 54 55 /* Scanner / Lexer */ 56 57 /* Values returned with some tokens, like yylval. */ 58 union lvalue { 59 struct sval string; 60 }; 61 62 enum rules_token { 63 TOK_END_OF_FILE = 0, 64 TOK_END_OF_LINE, 65 TOK_IDENTIFIER, 66 TOK_GROUP_NAME, 67 TOK_BANG, 68 TOK_EQUALS, 69 TOK_STAR, 70 TOK_ERROR 71 }; 72 73 static inline bool 74 is_ident(char ch) 75 { 76 return is_graph(ch) && ch != '\\'; 77 } 78 79 static enum rules_token 80 lex(struct scanner *s, union lvalue *val) 81 { 82 skip_more_whitespace_and_comments: 83 /* Skip spaces. */ 84 while (chr(s, ' ') || chr(s, '\t')); 85 86 /* Skip comments. */ 87 if (lit(s, "//")) { 88 skip_to_eol(s); 89 } 90 91 /* New line. */ 92 if (eol(s)) { 93 while (eol(s)) next(s); 94 return TOK_END_OF_LINE; 95 } 96 97 /* Escaped line continuation. */ 98 if (chr(s, '\\')) { 99 if (!eol(s)) { 100 scanner_err(s, "illegal new line escape; must appear at end of line"); 101 return TOK_ERROR; 102 } 103 next(s); 104 goto skip_more_whitespace_and_comments; 105 } 106 107 /* See if we're done. */ 108 if (eof(s)) return TOK_END_OF_FILE; 109 110 /* New token. */ 111 s->token_line = s->line; 112 s->token_column = s->column; 113 114 /* Operators and punctuation. */ 115 if (chr(s, '!')) return TOK_BANG; 116 if (chr(s, '=')) return TOK_EQUALS; 117 if (chr(s, '*')) return TOK_STAR; 118 119 /* Group name. */ 120 if (chr(s, '$')) { 121 val->string.start = s->s + s->pos; 122 val->string.len = 0; 123 while (is_ident(peek(s))) { 124 next(s); 125 val->string.len++; 126 } 127 if (val->string.len == 0) { 128 scanner_err(s, "unexpected character after \'$\'; expected name"); 129 return TOK_ERROR; 130 } 131 return TOK_GROUP_NAME; 132 } 133 134 /* Identifier. */ 135 if (is_ident(peek(s))) { 136 val->string.start = s->s + s->pos; 137 val->string.len = 0; 138 while (is_ident(peek(s))) { 139 next(s); 140 val->string.len++; 141 } 142 return TOK_IDENTIFIER; 143 } 144 145 scanner_err(s, "unrecognized token"); 146 return TOK_ERROR; 147 } 148 149 /***====================================================================***/ 150 151 enum rules_mlvo { 152 MLVO_MODEL, 153 MLVO_LAYOUT, 154 MLVO_VARIANT, 155 MLVO_OPTION, 156 _MLVO_NUM_ENTRIES 157 }; 158 159 #define SVAL_LIT(literal) { literal, sizeof(literal) - 1 } 160 161 static const struct sval rules_mlvo_svals[_MLVO_NUM_ENTRIES] = { 162 [MLVO_MODEL] = SVAL_LIT("model"), 163 [MLVO_LAYOUT] = SVAL_LIT("layout"), 164 [MLVO_VARIANT] = SVAL_LIT("variant"), 165 [MLVO_OPTION] = SVAL_LIT("option"), 166 }; 167 168 enum rules_kccgst { 169 KCCGST_KEYCODES, 170 KCCGST_TYPES, 171 KCCGST_COMPAT, 172 KCCGST_SYMBOLS, 173 KCCGST_GEOMETRY, 174 _KCCGST_NUM_ENTRIES 175 }; 176 177 static const struct sval rules_kccgst_svals[_KCCGST_NUM_ENTRIES] = { 178 [KCCGST_KEYCODES] = SVAL_LIT("keycodes"), 179 [KCCGST_TYPES] = SVAL_LIT("types"), 180 [KCCGST_COMPAT] = SVAL_LIT("compat"), 181 [KCCGST_SYMBOLS] = SVAL_LIT("symbols"), 182 [KCCGST_GEOMETRY] = SVAL_LIT("geometry"), 183 }; 184 185 /* We use this to keep score whether an mlvo was matched or not; if not, 186 * we warn the user that his preference was ignored. */ 187 struct matched_sval { 188 struct sval sval; 189 bool matched; 190 }; 191 typedef darray(struct matched_sval) darray_matched_sval; 192 193 /* 194 * A broken-down version of xkb_rule_names (without the rules, 195 * obviously). 196 */ 197 struct rule_names { 198 struct matched_sval model; 199 darray_matched_sval layouts; 200 darray_matched_sval variants; 201 darray_matched_sval options; 202 }; 203 204 struct group { 205 struct sval name; 206 darray_sval elements; 207 }; 208 209 struct mapping { 210 int mlvo_at_pos[_MLVO_NUM_ENTRIES]; 211 unsigned int num_mlvo; 212 unsigned int defined_mlvo_mask; 213 xkb_layout_index_t layout_idx, variant_idx; 214 int kccgst_at_pos[_KCCGST_NUM_ENTRIES]; 215 unsigned int num_kccgst; 216 unsigned int defined_kccgst_mask; 217 bool skip; 218 }; 219 220 enum mlvo_match_type { 221 MLVO_MATCH_NORMAL = 0, 222 MLVO_MATCH_WILDCARD, 223 MLVO_MATCH_GROUP, 224 }; 225 226 struct rule { 227 struct sval mlvo_value_at_pos[_MLVO_NUM_ENTRIES]; 228 enum mlvo_match_type match_type_at_pos[_MLVO_NUM_ENTRIES]; 229 unsigned int num_mlvo_values; 230 struct sval kccgst_value_at_pos[_KCCGST_NUM_ENTRIES]; 231 unsigned int num_kccgst_values; 232 bool skip; 233 }; 234 235 /* 236 * This is the main object used to match a given RMLVO against a rules 237 * file and aggragate the results in a KcCGST. It goes through a simple 238 * matching state machine, with tokens as transitions (see 239 * matcher_match()). 240 */ 241 struct matcher { 242 struct xkb_context *ctx; 243 /* Input.*/ 244 struct rule_names rmlvo; 245 union lvalue val; 246 struct scanner scanner; 247 darray(struct group) groups; 248 /* Current mapping. */ 249 struct mapping mapping; 250 /* Current rule. */ 251 struct rule rule; 252 /* Output. */ 253 darray_char kccgst[_KCCGST_NUM_ENTRIES]; 254 }; 255 256 static struct sval 257 strip_spaces(struct sval v) 258 { 259 while (v.len > 0 && is_space(v.start[0])) { v.len--; v.start++; } 260 while (v.len > 0 && is_space(v.start[v.len - 1])) v.len--; 261 return v; 262 } 263 264 static darray_matched_sval 265 split_comma_separated_mlvo(const char *s) 266 { 267 darray_matched_sval arr = darray_new(); 268 269 /* 270 * Make sure the array returned by this function always includes at 271 * least one value, e.g. "" -> { "" } and "," -> { "", "" }. 272 */ 273 274 if (!s) { 275 struct matched_sval val = { .sval = { NULL, 0 } }; 276 darray_append(arr, val); 277 return arr; 278 } 279 280 while (true) { 281 struct matched_sval val = { .sval = { s, 0 } }; 282 while (*s != '\0' && *s != ',') { s++; val.sval.len++; } 283 val.sval = strip_spaces(val.sval); 284 darray_append(arr, val); 285 if (*s == '\0') break; 286 if (*s == ',') s++; 287 } 288 289 return arr; 290 } 291 292 static struct matcher * 293 matcher_new(struct xkb_context *ctx, 294 const struct xkb_rule_names *rmlvo) 295 { 296 struct matcher *m = calloc(1, sizeof(*m)); 297 if (!m) 298 return NULL; 299 300 m->ctx = ctx; 301 m->rmlvo.model.sval.start = rmlvo->model; 302 m->rmlvo.model.sval.len = strlen_safe(rmlvo->model); 303 m->rmlvo.layouts = split_comma_separated_mlvo(rmlvo->layout); 304 m->rmlvo.variants = split_comma_separated_mlvo(rmlvo->variant); 305 m->rmlvo.options = split_comma_separated_mlvo(rmlvo->options); 306 307 return m; 308 } 309 310 static void 311 matcher_free(struct matcher *m) 312 { 313 struct group *group; 314 if (!m) 315 return; 316 darray_free(m->rmlvo.layouts); 317 darray_free(m->rmlvo.variants); 318 darray_free(m->rmlvo.options); 319 darray_foreach(group, m->groups) 320 darray_free(group->elements); 321 for (int i = 0; i < _KCCGST_NUM_ENTRIES; i++) 322 darray_free(m->kccgst[i]); 323 darray_free(m->groups); 324 free(m); 325 } 326 327 #define matcher_err(matcher, fmt, ...) \ 328 scanner_err(&(matcher)->scanner, fmt, ## __VA_ARGS__) 329 330 static void 331 matcher_group_start_new(struct matcher *m, struct sval name) 332 { 333 struct group group = { .name = name, .elements = darray_new() }; 334 darray_append(m->groups, group); 335 } 336 337 static void 338 matcher_group_add_element(struct matcher *m, struct sval element) 339 { 340 darray_append(darray_item(m->groups, darray_size(m->groups) - 1).elements, 341 element); 342 } 343 344 static void 345 matcher_mapping_start_new(struct matcher *m) 346 { 347 for (unsigned i = 0; i < _MLVO_NUM_ENTRIES; i++) 348 m->mapping.mlvo_at_pos[i] = -1; 349 for (unsigned i = 0; i < _KCCGST_NUM_ENTRIES; i++) 350 m->mapping.kccgst_at_pos[i] = -1; 351 m->mapping.layout_idx = m->mapping.variant_idx = XKB_LAYOUT_INVALID; 352 m->mapping.num_mlvo = m->mapping.num_kccgst = 0; 353 m->mapping.defined_mlvo_mask = 0; 354 m->mapping.defined_kccgst_mask = 0; 355 m->mapping.skip = false; 356 } 357 358 static int 359 extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out) 360 { 361 /* This function is pretty stupid, but works for now. */ 362 *out = XKB_LAYOUT_INVALID; 363 if (max_len < 3) 364 return -1; 365 if (s[0] != '[' || !is_digit(s[1]) || s[2] != ']') 366 return -1; 367 if (s[1] - '0' < 1 || s[1] - '0' > XKB_MAX_GROUPS) 368 return -1; 369 /* To zero-based index. */ 370 *out = s[1] - '0' - 1; 371 return 3; 372 } 373 374 static void 375 matcher_mapping_set_mlvo(struct matcher *m, struct sval ident) 376 { 377 enum rules_mlvo mlvo; 378 struct sval mlvo_sval; 379 380 for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) { 381 mlvo_sval = rules_mlvo_svals[mlvo]; 382 383 if (svaleq_prefix(mlvo_sval, ident)) 384 break; 385 } 386 387 /* Not found. */ 388 if (mlvo >= _MLVO_NUM_ENTRIES) { 389 matcher_err(m, "invalid mapping: %.*s is not a valid value here; ignoring rule set", 390 ident.len, ident.start); 391 m->mapping.skip = true; 392 return; 393 } 394 395 if (m->mapping.defined_mlvo_mask & (1u << mlvo)) { 396 matcher_err(m, "invalid mapping: %.*s appears twice on the same line; ignoring rule set", 397 mlvo_sval.len, mlvo_sval.start); 398 m->mapping.skip = true; 399 return; 400 } 401 402 /* If there are leftovers still, it must be an index. */ 403 if (mlvo_sval.len < ident.len) { 404 xkb_layout_index_t idx; 405 int consumed = extract_layout_index(ident.start + mlvo_sval.len, 406 ident.len - mlvo_sval.len, &idx); 407 if ((int) (ident.len - mlvo_sval.len) != consumed) { 408 matcher_err(m, "invalid mapping: \"%.*s\" may only be followed by a valid group index; ignoring rule set", 409 mlvo_sval.len, mlvo_sval.start); 410 m->mapping.skip = true; 411 return; 412 } 413 414 if (mlvo == MLVO_LAYOUT) { 415 m->mapping.layout_idx = idx; 416 } 417 else if (mlvo == MLVO_VARIANT) { 418 m->mapping.variant_idx = idx; 419 } 420 else { 421 matcher_err(m, "invalid mapping: \"%.*s\" cannot be followed by a group index; ignoring rule set", 422 mlvo_sval.len, mlvo_sval.start); 423 m->mapping.skip = true; 424 return; 425 } 426 } 427 428 m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo; 429 m->mapping.defined_mlvo_mask |= 1u << mlvo; 430 m->mapping.num_mlvo++; 431 } 432 433 static void 434 matcher_mapping_set_kccgst(struct matcher *m, struct sval ident) 435 { 436 enum rules_kccgst kccgst; 437 struct sval kccgst_sval; 438 439 for (kccgst = 0; kccgst < _KCCGST_NUM_ENTRIES; kccgst++) { 440 kccgst_sval = rules_kccgst_svals[kccgst]; 441 442 if (svaleq(rules_kccgst_svals[kccgst], ident)) 443 break; 444 } 445 446 /* Not found. */ 447 if (kccgst >= _KCCGST_NUM_ENTRIES) { 448 matcher_err(m, "invalid mapping: %.*s is not a valid value here; ignoring rule set", 449 ident.len, ident.start); 450 m->mapping.skip = true; 451 return; 452 } 453 454 if (m->mapping.defined_kccgst_mask & (1u << kccgst)) { 455 matcher_err(m, "invalid mapping: %.*s appears twice on the same line; ignoring rule set", 456 kccgst_sval.len, kccgst_sval.start); 457 m->mapping.skip = true; 458 return; 459 } 460 461 m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst; 462 m->mapping.defined_kccgst_mask |= 1u << kccgst; 463 m->mapping.num_kccgst++; 464 } 465 466 static void 467 matcher_mapping_verify(struct matcher *m) 468 { 469 if (m->mapping.num_mlvo == 0) { 470 matcher_err(m, "invalid mapping: must have at least one value on the left hand side; ignoring rule set"); 471 goto skip; 472 } 473 474 if (m->mapping.num_kccgst == 0) { 475 matcher_err(m, "invalid mapping: must have at least one value on the right hand side; ignoring rule set"); 476 goto skip; 477 } 478 479 /* 480 * This following is very stupid, but this is how it works. 481 * See the "Notes" section in the overview above. 482 */ 483 484 if (m->mapping.defined_mlvo_mask & (1u << MLVO_LAYOUT)) { 485 if (m->mapping.layout_idx == XKB_LAYOUT_INVALID) { 486 if (darray_size(m->rmlvo.layouts) > 1) 487 goto skip; 488 } 489 else { 490 if (darray_size(m->rmlvo.layouts) == 1 || 491 m->mapping.layout_idx >= darray_size(m->rmlvo.layouts)) 492 goto skip; 493 } 494 } 495 496 if (m->mapping.defined_mlvo_mask & (1u << MLVO_VARIANT)) { 497 if (m->mapping.variant_idx == XKB_LAYOUT_INVALID) { 498 if (darray_size(m->rmlvo.variants) > 1) 499 goto skip; 500 } 501 else { 502 if (darray_size(m->rmlvo.variants) == 1 || 503 m->mapping.variant_idx >= darray_size(m->rmlvo.variants)) 504 goto skip; 505 } 506 } 507 508 return; 509 510 skip: 511 m->mapping.skip = true; 512 } 513 514 static void 515 matcher_rule_start_new(struct matcher *m) 516 { 517 memset(&m->rule, 0, sizeof(m->rule)); 518 m->rule.skip = m->mapping.skip; 519 } 520 521 static void 522 matcher_rule_set_mlvo_common(struct matcher *m, struct sval ident, 523 enum mlvo_match_type match_type) 524 { 525 if (m->rule.num_mlvo_values + 1 > m->mapping.num_mlvo) { 526 matcher_err(m, "invalid rule: has more values than the mapping line; ignoring rule"); 527 m->rule.skip = true; 528 return; 529 } 530 m->rule.match_type_at_pos[m->rule.num_mlvo_values] = match_type; 531 m->rule.mlvo_value_at_pos[m->rule.num_mlvo_values] = ident; 532 m->rule.num_mlvo_values++; 533 } 534 535 static void 536 matcher_rule_set_mlvo_wildcard(struct matcher *m) 537 { 538 struct sval dummy = { NULL, 0 }; 539 matcher_rule_set_mlvo_common(m, dummy, MLVO_MATCH_WILDCARD); 540 } 541 542 static void 543 matcher_rule_set_mlvo_group(struct matcher *m, struct sval ident) 544 { 545 matcher_rule_set_mlvo_common(m, ident, MLVO_MATCH_GROUP); 546 } 547 548 static void 549 matcher_rule_set_mlvo(struct matcher *m, struct sval ident) 550 { 551 matcher_rule_set_mlvo_common(m, ident, MLVO_MATCH_NORMAL); 552 } 553 554 static void 555 matcher_rule_set_kccgst(struct matcher *m, struct sval ident) 556 { 557 if (m->rule.num_kccgst_values + 1 > m->mapping.num_kccgst) { 558 matcher_err(m, "invalid rule: has more values than the mapping line; ignoring rule"); 559 m->rule.skip = true; 560 return; 561 } 562 m->rule.kccgst_value_at_pos[m->rule.num_kccgst_values] = ident; 563 m->rule.num_kccgst_values++; 564 } 565 566 static bool 567 match_group(struct matcher *m, struct sval group_name, struct sval to) 568 { 569 struct group *group; 570 struct sval *element; 571 bool found = false; 572 573 darray_foreach(group, m->groups) { 574 if (svaleq(group->name, group_name)) { 575 found = true; 576 break; 577 } 578 } 579 580 if (!found) { 581 /* 582 * rules/evdev intentionally uses some undeclared group names 583 * in rules (e.g. commented group definitions which may be 584 * uncommented if needed). So we continue silently. 585 */ 586 return false; 587 } 588 589 darray_foreach(element, group->elements) 590 if (svaleq(to, *element)) 591 return true; 592 593 return false; 594 } 595 596 static bool 597 match_value(struct matcher *m, struct sval val, struct sval to, 598 enum mlvo_match_type match_type) 599 { 600 if (match_type == MLVO_MATCH_WILDCARD) 601 return true; 602 if (match_type == MLVO_MATCH_GROUP) 603 return match_group(m, val, to); 604 return svaleq(val, to); 605 } 606 607 static bool 608 match_value_and_mark(struct matcher *m, struct sval val, 609 struct matched_sval *to, enum mlvo_match_type match_type) 610 { 611 bool matched = match_value(m, val, to->sval, match_type); 612 if (matched) 613 to->matched = true; 614 return matched; 615 } 616 617 /* 618 * This function performs %-expansion on @value (see overview above), 619 * and appends the result to @to. 620 */ 621 static bool 622 append_expanded_kccgst_value(struct matcher *m, darray_char *to, 623 struct sval value) 624 { 625 const char *s = value.start; 626 darray_char expanded = darray_new(); 627 char ch; 628 bool expanded_plus, to_plus; 629 630 /* 631 * Some ugly hand-lexing here, but going through the scanner is more 632 * trouble than it's worth, and the format is ugly on its own merit. 633 */ 634 for (unsigned i = 0; i < value.len; ) { 635 enum rules_mlvo mlv; 636 xkb_layout_index_t idx; 637 char pfx, sfx; 638 struct matched_sval *expanded_value; 639 640 /* Check if that's a start of an expansion. */ 641 if (s[i] != '%') { 642 /* Just a normal character. */ 643 darray_appends_nullterminate(expanded, &s[i++], 1); 644 continue; 645 } 646 if (++i >= value.len) goto error; 647 648 pfx = sfx = 0; 649 650 /* Check for prefix. */ 651 if (s[i] == '(' || s[i] == '+' || s[i] == '|' || 652 s[i] == '_' || s[i] == '-') { 653 pfx = s[i]; 654 if (s[i] == '(') sfx = ')'; 655 if (++i >= value.len) goto error; 656 } 657 658 /* Mandatory model/layout/variant specifier. */ 659 switch (s[i++]) { 660 case 'm': mlv = MLVO_MODEL; break; 661 case 'l': mlv = MLVO_LAYOUT; break; 662 case 'v': mlv = MLVO_VARIANT; break; 663 default: goto error; 664 } 665 666 /* Check for index. */ 667 idx = XKB_LAYOUT_INVALID; 668 if (i < value.len && s[i] == '[') { 669 int consumed; 670 671 if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) { 672 matcher_err(m, "invalid index in %%-expansion; may only index layout or variant"); 673 goto error; 674 } 675 676 consumed = extract_layout_index(s + i, value.len - i, &idx); 677 if (consumed == -1) goto error; 678 i += consumed; 679 } 680 681 /* Check for suffix, if there supposed to be one. */ 682 if (sfx != 0) { 683 if (i >= value.len) goto error; 684 if (s[i++] != sfx) goto error; 685 } 686 687 /* Get the expanded value. */ 688 expanded_value = NULL; 689 690 if (mlv == MLVO_LAYOUT) { 691 if (idx != XKB_LAYOUT_INVALID && 692 idx < darray_size(m->rmlvo.layouts) && 693 darray_size(m->rmlvo.layouts) > 1) 694 expanded_value = &darray_item(m->rmlvo.layouts, idx); 695 else if (idx == XKB_LAYOUT_INVALID && 696 darray_size(m->rmlvo.layouts) == 1) 697 expanded_value = &darray_item(m->rmlvo.layouts, 0); 698 } 699 else if (mlv == MLVO_VARIANT) { 700 if (idx != XKB_LAYOUT_INVALID && 701 idx < darray_size(m->rmlvo.variants) && 702 darray_size(m->rmlvo.variants) > 1) 703 expanded_value = &darray_item(m->rmlvo.variants, idx); 704 else if (idx == XKB_LAYOUT_INVALID && 705 darray_size(m->rmlvo.variants) == 1) 706 expanded_value = &darray_item(m->rmlvo.variants, 0); 707 } 708 else if (mlv == MLVO_MODEL) { 709 expanded_value = &m->rmlvo.model; 710 } 711 712 /* If we didn't get one, skip silently. */ 713 if (!expanded_value || expanded_value->sval.len == 0) 714 continue; 715 716 if (pfx != 0) 717 darray_appends_nullterminate(expanded, &pfx, 1); 718 darray_appends_nullterminate(expanded, 719 expanded_value->sval.start, 720 expanded_value->sval.len); 721 if (sfx != 0) 722 darray_appends_nullterminate(expanded, &sfx, 1); 723 expanded_value->matched = true; 724 } 725 726 /* 727 * Appending bar to foo -> foo (not an error if this happens) 728 * Appending +bar to foo -> foo+bar 729 * Appending bar to +foo -> bar+foo 730 * Appending +bar to +foo -> +foo+bar 731 */ 732 733 ch = (darray_empty(expanded) ? '\0' : darray_item(expanded, 0)); 734 expanded_plus = (ch == '+' || ch == '|'); 735 ch = (darray_empty(*to) ? '\0' : darray_item(*to, 0)); 736 to_plus = (ch == '+' || ch == '|'); 737 738 if (expanded_plus || darray_empty(*to)) 739 darray_appends_nullterminate(*to, expanded.item, expanded.size); 740 else if (to_plus) 741 darray_prepends_nullterminate(*to, expanded.item, expanded.size); 742 743 darray_free(expanded); 744 return true; 745 746 error: 747 darray_free(expanded); 748 matcher_err(m, "invalid %%-expansion in value; not used"); 749 return false; 750 } 751 752 static void 753 matcher_rule_verify(struct matcher *m) 754 { 755 if (m->rule.num_mlvo_values != m->mapping.num_mlvo || 756 m->rule.num_kccgst_values != m->mapping.num_kccgst) { 757 matcher_err(m, "invalid rule: must have same number of values as mapping line; ignoring rule"); 758 m->rule.skip = true; 759 } 760 } 761 762 static void 763 matcher_rule_apply_if_matches(struct matcher *m) 764 { 765 for (unsigned i = 0; i < m->mapping.num_mlvo; i++) { 766 enum rules_mlvo mlvo = m->mapping.mlvo_at_pos[i]; 767 struct sval value = m->rule.mlvo_value_at_pos[i]; 768 enum mlvo_match_type match_type = m->rule.match_type_at_pos[i]; 769 struct matched_sval *to; 770 bool matched = false; 771 772 if (mlvo == MLVO_MODEL) { 773 to = &m->rmlvo.model; 774 matched = match_value_and_mark(m, value, to, match_type); 775 } 776 else if (mlvo == MLVO_LAYOUT) { 777 xkb_layout_index_t idx = m->mapping.layout_idx; 778 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx); 779 to = &darray_item(m->rmlvo.layouts, idx); 780 matched = match_value_and_mark(m, value, to, match_type); 781 } 782 else if (mlvo == MLVO_VARIANT) { 783 xkb_layout_index_t idx = m->mapping.layout_idx; 784 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx); 785 to = &darray_item(m->rmlvo.variants, idx); 786 matched = match_value_and_mark(m, value, to, match_type); 787 } 788 else if (mlvo == MLVO_OPTION) { 789 darray_foreach(to, m->rmlvo.options) { 790 matched = match_value_and_mark(m, value, to, match_type); 791 if (matched) 792 break; 793 } 794 } 795 796 if (!matched) 797 return; 798 } 799 800 for (unsigned i = 0; i < m->mapping.num_kccgst; i++) { 801 enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i]; 802 struct sval value = m->rule.kccgst_value_at_pos[i]; 803 append_expanded_kccgst_value(m, &m->kccgst[kccgst], value); 804 } 805 806 /* 807 * If a rule matches in a rule set, the rest of the set should be 808 * skipped. However, rule sets matching against options may contain 809 * several legitimate rules, so they are processed entirely. 810 */ 811 if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION))) 812 m->mapping.skip = true; 813 } 814 815 static enum rules_token 816 gettok(struct matcher *m) 817 { 818 return lex(&m->scanner, &m->val); 819 } 820 821 static bool 822 matcher_match(struct matcher *m, const char *string, size_t len, 823 const char *file_name, struct xkb_component_names *out) 824 { 825 enum rules_token tok; 826 struct matched_sval *mval; 827 828 if (!m) 829 return false; 830 831 scanner_init(&m->scanner, m->ctx, string, len, file_name, NULL); 832 833 initial: 834 switch (tok = gettok(m)) { 835 case TOK_BANG: 836 goto bang; 837 case TOK_END_OF_LINE: 838 goto initial; 839 case TOK_END_OF_FILE: 840 goto finish; 841 default: 842 goto unexpected; 843 } 844 845 bang: 846 switch (tok = gettok(m)) { 847 case TOK_GROUP_NAME: 848 matcher_group_start_new(m, m->val.string); 849 goto group_name; 850 case TOK_IDENTIFIER: 851 matcher_mapping_start_new(m); 852 matcher_mapping_set_mlvo(m, m->val.string); 853 goto mapping_mlvo; 854 default: 855 goto unexpected; 856 } 857 858 group_name: 859 switch (tok = gettok(m)) { 860 case TOK_EQUALS: 861 goto group_element; 862 default: 863 goto unexpected; 864 } 865 866 group_element: 867 switch (tok = gettok(m)) { 868 case TOK_IDENTIFIER: 869 matcher_group_add_element(m, m->val.string); 870 goto group_element; 871 case TOK_END_OF_LINE: 872 goto initial; 873 default: 874 goto unexpected; 875 } 876 877 mapping_mlvo: 878 switch (tok = gettok(m)) { 879 case TOK_IDENTIFIER: 880 if (!m->mapping.skip) 881 matcher_mapping_set_mlvo(m, m->val.string); 882 goto mapping_mlvo; 883 case TOK_EQUALS: 884 goto mapping_kccgst; 885 default: 886 goto unexpected; 887 } 888 889 mapping_kccgst: 890 switch (tok = gettok(m)) { 891 case TOK_IDENTIFIER: 892 if (!m->mapping.skip) 893 matcher_mapping_set_kccgst(m, m->val.string); 894 goto mapping_kccgst; 895 case TOK_END_OF_LINE: 896 if (!m->mapping.skip) 897 matcher_mapping_verify(m); 898 goto rule_mlvo_first; 899 default: 900 goto unexpected; 901 } 902 903 rule_mlvo_first: 904 switch (tok = gettok(m)) { 905 case TOK_BANG: 906 goto bang; 907 case TOK_END_OF_LINE: 908 goto rule_mlvo_first; 909 case TOK_END_OF_FILE: 910 goto finish; 911 default: 912 matcher_rule_start_new(m); 913 goto rule_mlvo_no_tok; 914 } 915 916 rule_mlvo: 917 tok = gettok(m); 918 rule_mlvo_no_tok: 919 switch (tok) { 920 case TOK_IDENTIFIER: 921 if (!m->rule.skip) 922 matcher_rule_set_mlvo(m, m->val.string); 923 goto rule_mlvo; 924 case TOK_STAR: 925 if (!m->rule.skip) 926 matcher_rule_set_mlvo_wildcard(m); 927 goto rule_mlvo; 928 case TOK_GROUP_NAME: 929 if (!m->rule.skip) 930 matcher_rule_set_mlvo_group(m, m->val.string); 931 goto rule_mlvo; 932 case TOK_EQUALS: 933 goto rule_kccgst; 934 default: 935 goto unexpected; 936 } 937 938 rule_kccgst: 939 switch (tok = gettok(m)) { 940 case TOK_IDENTIFIER: 941 if (!m->rule.skip) 942 matcher_rule_set_kccgst(m, m->val.string); 943 goto rule_kccgst; 944 case TOK_END_OF_LINE: 945 if (!m->rule.skip) 946 matcher_rule_verify(m); 947 if (!m->rule.skip) 948 matcher_rule_apply_if_matches(m); 949 goto rule_mlvo_first; 950 default: 951 goto unexpected; 952 } 953 954 unexpected: 955 switch (tok) { 956 case TOK_ERROR: 957 goto error; 958 default: 959 goto state_error; 960 } 961 962 finish: 963 if (darray_empty(m->kccgst[KCCGST_KEYCODES]) || 964 darray_empty(m->kccgst[KCCGST_TYPES]) || 965 darray_empty(m->kccgst[KCCGST_COMPAT]) || 966 /* darray_empty(m->kccgst[KCCGST_GEOMETRY]) || */ 967 darray_empty(m->kccgst[KCCGST_SYMBOLS])) 968 goto error; 969 970 darray_steal(m->kccgst[KCCGST_KEYCODES], &out->keycodes, NULL); 971 darray_steal(m->kccgst[KCCGST_TYPES], &out->types, NULL); 972 darray_steal(m->kccgst[KCCGST_COMPAT], &out->compat, NULL); 973 darray_steal(m->kccgst[KCCGST_SYMBOLS], &out->symbols, NULL); 974 darray_free(m->kccgst[KCCGST_GEOMETRY]); 975 976 977 mval = &m->rmlvo.model; 978 if (!mval->matched && mval->sval.len > 0) 979 log_err(m->ctx, "Unrecognized RMLVO model \"%.*s\" was ignored\n", 980 mval->sval.len, mval->sval.start); 981 darray_foreach(mval, m->rmlvo.layouts) 982 if (!mval->matched && mval->sval.len > 0) 983 log_err(m->ctx, "Unrecognized RMLVO layout \"%.*s\" was ignored\n", 984 mval->sval.len, mval->sval.start); 985 darray_foreach(mval, m->rmlvo.variants) 986 if (!mval->matched && mval->sval.len > 0) 987 log_err(m->ctx, "Unrecognized RMLVO variant \"%.*s\" was ignored\n", 988 mval->sval.len, mval->sval.start); 989 darray_foreach(mval, m->rmlvo.options) 990 if (!mval->matched && mval->sval.len > 0) 991 log_err(m->ctx, "Unrecognized RMLVO option \"%.*s\" was ignored\n", 992 mval->sval.len, mval->sval.start); 993 994 return true; 995 996 state_error: 997 matcher_err(m, "unexpected token"); 998 error: 999 return false; 1000 } 1001 1002 bool 1003 xkb_components_from_rules(struct xkb_context *ctx, 1004 const struct xkb_rule_names *rmlvo, 1005 struct xkb_component_names *out) 1006 { 1007 bool ret = false; 1008 FILE *file; 1009 char *path; 1010 const char *string; 1011 size_t size; 1012 struct matcher *matcher; 1013 1014 file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path); 1015 if (!file) 1016 goto err_out; 1017 1018 ret = map_file(file, &string, &size); 1019 if (!ret) { 1020 log_err(ctx, "Couldn't read rules file \"%s\": %s\n", 1021 path, strerror(errno)); 1022 goto err_file; 1023 } 1024 1025 matcher = matcher_new(ctx, rmlvo); 1026 ret = matcher_match(matcher, string, size, path, out); 1027 if (!ret) 1028 log_err(ctx, "No components returned from XKB rules \"%s\"\n", path); 1029 matcher_free(matcher); 1030 1031 unmap_file(string, size); 1032 err_file: 1033 free(path); 1034 fclose(file); 1035 err_out: 1036 return ret; 1037 } 1038