1 /*
2 * Copyright © 2013 Ran Benita <ran234@gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /******************************************************************
25
26 Copyright 1992 by Oki Technosystems Laboratory, Inc.
27 Copyright 1992 by Fuji Xerox Co., Ltd.
28
29 Permission to use, copy, modify, distribute, and sell this software
30 and its documentation for any purpose is hereby granted without fee,
31 provided that the above copyright notice appear in all copies and
32 that both that copyright notice and this permission notice appear
33 in supporting documentation, and that the name of Oki Technosystems
34 Laboratory and Fuji Xerox not be used in advertising or publicity
35 pertaining to distribution of the software without specific, written
36 prior permission.
37 Oki Technosystems Laboratory and Fuji Xerox make no representations
38 about the suitability of this software for any purpose. It is provided
39 "as is" without express or implied warranty.
40
41 OKI TECHNOSYSTEMS LABORATORY AND FUJI XEROX DISCLAIM ALL WARRANTIES
42 WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
43 MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OKI TECHNOSYSTEMS
44 LABORATORY AND FUJI XEROX BE LIABLE FOR ANY SPECIAL, INDIRECT OR
45 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
46 OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
47 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
48 OR PERFORMANCE OF THIS SOFTWARE.
49
50 Author: Yasuhiro Kawai Oki Technosystems Laboratory
51 Author: Kazunori Nishihara Fuji Xerox
52
53 ******************************************************************/
54
55 #include <errno.h>
56
57 #include "utils.h"
58 #include "scanner-utils.h"
59 #include "table.h"
60 #include "paths.h"
61 #include "utf8.h"
62 #include "parser.h"
63
64 #define MAX_LHS_LEN 10
65 #define MAX_INCLUDE_DEPTH 5
66
67 #define KEYSYM_FROM_NAME_CACHE_SIZE 8
68
69 /*
70 * xkb_keysym_from_name() is fairly slow, because for internal reasons
71 * it must use strcasecmp().
72 * A small cache reduces about 20% from the compilation time of
73 * en_US.UTF-8/Compose.
74 */
75 struct keysym_from_name_cache {
76 struct {
77 char name[64];
78 unsigned len;
79 xkb_keysym_t keysym;
80 } cache[KEYSYM_FROM_NAME_CACHE_SIZE];
81 unsigned next;
82 };
83
84 static xkb_keysym_t
cached_keysym_from_name(struct keysym_from_name_cache * cache,const char * name,size_t len)85 cached_keysym_from_name(struct keysym_from_name_cache *cache,
86 const char *name, size_t len)
87 {
88 xkb_keysym_t keysym;
89
90 if (len >= sizeof(cache->cache[0].name))
91 return XKB_KEY_NoSymbol;
92
93 for (unsigned i = 0; i < KEYSYM_FROM_NAME_CACHE_SIZE; i++)
94 if (cache->cache[i].len == len &&
95 memcmp(cache->cache[i].name, name, len) == 0)
96 return cache->cache[i].keysym;
97
98 keysym = xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS);
99 strcpy(cache->cache[cache->next].name, name);
100 cache->cache[cache->next].len = len;
101 cache->cache[cache->next].keysym = keysym;
102 cache->next = (cache->next + 1) % KEYSYM_FROM_NAME_CACHE_SIZE;
103 return keysym;
104 }
105
106 /*
107 * Grammar adapted from libX11/modules/im/ximcp/imLcPrs.c.
108 * See also the XCompose(5) manpage.
109 *
110 * FILE ::= { [PRODUCTION] [COMMENT] "\n" | INCLUDE }
111 * INCLUDE ::= "include" '"' INCLUDE_STRING '"'
112 * PRODUCTION ::= LHS ":" RHS [ COMMENT ]
113 * COMMENT ::= "#" {<any character except null or newline>}
114 * LHS ::= EVENT { EVENT }
115 * EVENT ::= [MODIFIER_LIST] "<" keysym ">"
116 * MODIFIER_LIST ::= ("!" {MODIFIER} ) | "None"
117 * MODIFIER ::= ["~"] modifier_name
118 * RHS ::= ( STRING | keysym | STRING keysym )
119 * STRING ::= '"' { CHAR } '"'
120 * CHAR ::= GRAPHIC_CHAR | ESCAPED_CHAR
121 * GRAPHIC_CHAR ::= locale (codeset) dependent code
122 * ESCAPED_CHAR ::= ('\\' | '\"' | OCTAL | HEX )
123 * OCTAL ::= '\' OCTAL_CHAR [OCTAL_CHAR [OCTAL_CHAR]]
124 * OCTAL_CHAR ::= (0|1|2|3|4|5|6|7)
125 * HEX ::= '\' (x|X) HEX_CHAR [HEX_CHAR]]
126 * HEX_CHAR ::= (0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|a|b|c|d|e|f)
127 *
128 * INCLUDE_STRING is a filesystem path, with the following %-expansions:
129 * %% - '%'.
130 * %H - The user's home directory (the $HOME environment variable).
131 * %L - The name of the locale specific Compose file (e.g.,
132 * "/usr/share/X11/locale/<localename>/Compose").
133 * %S - The name of the system directory for Compose files (e.g.,
134 * "/usr/share/X11/locale").
135 */
136
137 enum rules_token {
138 TOK_END_OF_FILE = 0,
139 TOK_END_OF_LINE,
140 TOK_INCLUDE,
141 TOK_INCLUDE_STRING,
142 TOK_LHS_KEYSYM,
143 TOK_COLON,
144 TOK_BANG,
145 TOK_TILDE,
146 TOK_STRING,
147 TOK_IDENT,
148 TOK_ERROR
149 };
150
151 /* Values returned with some tokens, like yylval. */
152 union lvalue {
153 struct {
154 /* Still \0-terminated. */
155 const char *str;
156 size_t len;
157 } string;
158 };
159
160 static enum rules_token
lex(struct scanner * s,union lvalue * val)161 lex(struct scanner *s, union lvalue *val)
162 {
163 skip_more_whitespace_and_comments:
164 /* Skip spaces. */
165 while (is_space(peek(s)))
166 if (next(s) == '\n')
167 return TOK_END_OF_LINE;
168
169 /* Skip comments. */
170 if (chr(s, '#')) {
171 skip_to_eol(s);
172 goto skip_more_whitespace_and_comments;
173 }
174
175 /* See if we're done. */
176 if (eof(s)) return TOK_END_OF_FILE;
177
178 /* New token. */
179 s->token_line = s->line;
180 s->token_column = s->column;
181 s->buf_pos = 0;
182
183 /* LHS Keysym. */
184 if (chr(s, '<')) {
185 while (peek(s) != '>' && !eol(s))
186 buf_append(s, next(s));
187 if (!chr(s, '>')) {
188 scanner_err(s, "unterminated keysym literal");
189 return TOK_ERROR;
190 }
191 if (!buf_append(s, '\0')) {
192 scanner_err(s, "keysym literal is too long");
193 return TOK_ERROR;
194 }
195 val->string.str = s->buf;
196 val->string.len = s->buf_pos;
197 return TOK_LHS_KEYSYM;
198 }
199
200 /* Colon. */
201 if (chr(s, ':'))
202 return TOK_COLON;
203 if (chr(s, '!'))
204 return TOK_BANG;
205 if (chr(s, '~'))
206 return TOK_TILDE;
207
208 /* String literal. */
209 if (chr(s, '\"')) {
210 while (!eof(s) && !eol(s) && peek(s) != '\"') {
211 if (chr(s, '\\')) {
212 uint8_t o;
213 if (chr(s, '\\')) {
214 buf_append(s, '\\');
215 }
216 else if (chr(s, '"')) {
217 buf_append(s, '"');
218 }
219 else if (chr(s, 'x') || chr(s, 'X')) {
220 if (hex(s, &o))
221 buf_append(s, (char) o);
222 else
223 scanner_warn(s, "illegal hexadecimal escape sequence in string literal");
224 }
225 else if (oct(s, &o)) {
226 buf_append(s, (char) o);
227 }
228 else {
229 scanner_warn(s, "unknown escape sequence (%c) in string literal", peek(s));
230 /* Ignore. */
231 }
232 } else {
233 buf_append(s, next(s));
234 }
235 }
236 if (!chr(s, '\"')) {
237 scanner_err(s, "unterminated string literal");
238 return TOK_ERROR;
239 }
240 if (!buf_append(s, '\0')) {
241 scanner_err(s, "string literal is too long");
242 return TOK_ERROR;
243 }
244 if (!is_valid_utf8(s->buf, s->buf_pos - 1)) {
245 scanner_err(s, "string literal is not a valid UTF-8 string");
246 return TOK_ERROR;
247 }
248 val->string.str = s->buf;
249 val->string.len = s->buf_pos;
250 return TOK_STRING;
251 }
252
253 /* Identifier or include. */
254 if (is_alpha(peek(s)) || peek(s) == '_') {
255 s->buf_pos = 0;
256 while (is_alnum(peek(s)) || peek(s) == '_')
257 buf_append(s, next(s));
258 if (!buf_append(s, '\0')) {
259 scanner_err(s, "identifier is too long");
260 return TOK_ERROR;
261 }
262
263 if (streq(s->buf, "include"))
264 return TOK_INCLUDE;
265
266 val->string.str = s->buf;
267 val->string.len = s->buf_pos;
268 return TOK_IDENT;
269 }
270
271 /* Discard rest of line. */
272 skip_to_eol(s);
273
274 scanner_err(s, "unrecognized token");
275 return TOK_ERROR;
276 }
277
278 static enum rules_token
lex_include_string(struct scanner * s,struct xkb_compose_table * table,union lvalue * val_out)279 lex_include_string(struct scanner *s, struct xkb_compose_table *table,
280 union lvalue *val_out)
281 {
282 while (is_space(peek(s)))
283 if (next(s) == '\n')
284 return TOK_END_OF_LINE;
285
286 s->token_line = s->line;
287 s->token_column = s->column;
288 s->buf_pos = 0;
289
290 if (!chr(s, '\"')) {
291 scanner_err(s, "include statement must be followed by a path");
292 return TOK_ERROR;
293 }
294
295 while (!eof(s) && !eol(s) && peek(s) != '\"') {
296 if (chr(s, '%')) {
297 if (chr(s, '%')) {
298 buf_append(s, '%');
299 }
300 else if (chr(s, 'H')) {
301 const char *home = secure_getenv("HOME");
302 if (!home) {
303 scanner_err(s, "%%H was used in an include statement, but the HOME environment variable is not set");
304 return TOK_ERROR;
305 }
306 if (!buf_appends(s, home)) {
307 scanner_err(s, "include path after expanding %%H is too long");
308 return TOK_ERROR;
309 }
310 }
311 else if (chr(s, 'L')) {
312 char *path = get_locale_compose_file_path(table->locale);
313 if (!path) {
314 scanner_err(s, "failed to expand %%L to the locale Compose file");
315 return TOK_ERROR;
316 }
317 if (!buf_appends(s, path)) {
318 free(path);
319 scanner_err(s, "include path after expanding %%L is too long");
320 return TOK_ERROR;
321 }
322 free(path);
323 }
324 else if (chr(s, 'S')) {
325 const char *xlocaledir = get_xlocaledir_path();
326 if (!buf_appends(s, xlocaledir)) {
327 scanner_err(s, "include path after expanding %%S is too long");
328 return TOK_ERROR;
329 }
330 }
331 else {
332 scanner_err(s, "unknown %% format (%c) in include statement", peek(s));
333 return TOK_ERROR;
334 }
335 } else {
336 buf_append(s, next(s));
337 }
338 }
339 if (!chr(s, '\"')) {
340 scanner_err(s, "unterminated include statement");
341 return TOK_ERROR;
342 }
343 if (!buf_append(s, '\0')) {
344 scanner_err(s, "include path is too long");
345 return TOK_ERROR;
346 }
347 val_out->string.str = s->buf;
348 val_out->string.len = s->buf_pos;
349 return TOK_INCLUDE_STRING;
350 }
351
352 struct production {
353 xkb_keysym_t lhs[MAX_LHS_LEN];
354 unsigned int len;
355 xkb_keysym_t keysym;
356 char string[256];
357 bool has_keysym;
358 bool has_string;
359
360 xkb_mod_mask_t mods;
361 xkb_mod_mask_t modmask;
362 };
363
364 static uint32_t
add_node(struct xkb_compose_table * table,xkb_keysym_t keysym)365 add_node(struct xkb_compose_table *table, xkb_keysym_t keysym)
366 {
367 struct compose_node new = {
368 .keysym = keysym,
369 .next = 0,
370 .is_leaf = true,
371 };
372 darray_append(table->nodes, new);
373 return darray_size(table->nodes) - 1;
374 }
375
376 static void
add_production(struct xkb_compose_table * table,struct scanner * s,const struct production * production)377 add_production(struct xkb_compose_table *table, struct scanner *s,
378 const struct production *production)
379 {
380 unsigned lhs_pos;
381 uint32_t curr;
382 struct compose_node *node;
383
384 curr = 0;
385 node = &darray_item(table->nodes, curr);
386
387 /*
388 * Insert the sequence to the trie, creating new nodes as needed.
389 *
390 * TODO: This can be sped up a bit by first trying the path that the
391 * previous production took, and only then doing the linear search
392 * through the trie levels. This will work because sequences in the
393 * Compose files are often clustered by a common prefix; especially
394 * in the 1st and 2nd keysyms, which is where the largest variation
395 * (thus, longest search) is.
396 */
397 for (lhs_pos = 0; lhs_pos < production->len; lhs_pos++) {
398 while (production->lhs[lhs_pos] != node->keysym) {
399 if (node->next == 0) {
400 uint32_t next = add_node(table, production->lhs[lhs_pos]);
401 /* Refetch since add_node could have realloc()ed. */
402 node = &darray_item(table->nodes, curr);
403 node->next = next;
404 }
405
406 curr = node->next;
407 node = &darray_item(table->nodes, curr);
408 }
409
410 if (lhs_pos + 1 == production->len)
411 break;
412
413 if (node->is_leaf) {
414 if (node->u.leaf.utf8 != 0 ||
415 node->u.leaf.keysym != XKB_KEY_NoSymbol) {
416 scanner_warn(s, "a sequence already exists which is a prefix of this sequence; overriding");
417 node->u.leaf.utf8 = 0;
418 node->u.leaf.keysym = XKB_KEY_NoSymbol;
419 }
420
421 {
422 uint32_t successor = add_node(table, production->lhs[lhs_pos + 1]);
423 /* Refetch since add_node could have realloc()ed. */
424 node = &darray_item(table->nodes, curr);
425 node->is_leaf = false;
426 node->u.successor = successor;
427 }
428 }
429
430 curr = node->u.successor;
431 node = &darray_item(table->nodes, curr);
432 }
433
434 if (!node->is_leaf) {
435 scanner_warn(s, "this compose sequence is a prefix of another; skipping line");
436 return;
437 }
438
439 if (node->u.leaf.utf8 != 0 || node->u.leaf.keysym != XKB_KEY_NoSymbol) {
440 if (streq(&darray_item(table->utf8, node->u.leaf.utf8),
441 production->string) &&
442 node->u.leaf.keysym == production->keysym) {
443 scanner_warn(s, "this compose sequence is a duplicate of another; skipping line");
444 return;
445 }
446 scanner_warn(s, "this compose sequence already exists; overriding");
447 }
448
449 if (production->has_string) {
450 node->u.leaf.utf8 = darray_size(table->utf8);
451 darray_append_items(table->utf8, production->string,
452 strlen(production->string) + 1);
453 }
454 if (production->has_keysym) {
455 node->u.leaf.keysym = production->keysym;
456 }
457 }
458
459 static xkb_mod_index_t
resolve_modifier(const char * name)460 resolve_modifier(const char *name)
461 {
462 static const struct {
463 const char *name;
464 xkb_mod_index_t mod;
465 } mods[] = {
466 { "Shift", 0 },
467 { "Ctrl", 2 },
468 { "Alt", 3 },
469 { "Meta", 3 },
470 { "Lock", 1 },
471 { "Caps", 1 },
472 };
473
474 for (unsigned i = 0; i < ARRAY_SIZE(mods); i++)
475 if (streq(name, mods[i].name))
476 return mods[i].mod;
477
478 return XKB_MOD_INVALID;
479 }
480
481 static bool
482 parse(struct xkb_compose_table *table, struct scanner *s,
483 unsigned include_depth);
484
485 static bool
do_include(struct xkb_compose_table * table,struct scanner * s,const char * path,unsigned include_depth)486 do_include(struct xkb_compose_table *table, struct scanner *s,
487 const char *path, unsigned include_depth)
488 {
489 FILE *file;
490 bool ok;
491 const char *string;
492 size_t size;
493 struct scanner new_s;
494
495 if (include_depth >= MAX_INCLUDE_DEPTH) {
496 scanner_err(s, "maximum include depth (%d) exceeded; maybe there is an include loop?",
497 MAX_INCLUDE_DEPTH);
498 return false;
499 }
500
501 file = fopen(path, "r");
502 if (!file) {
503 scanner_err(s, "failed to open included Compose file \"%s\": %s",
504 path, strerror(errno));
505 return false;
506 }
507
508 ok = map_file(file, &string, &size);
509 if (!ok) {
510 scanner_err(s, "failed to read included Compose file \"%s\": %s",
511 path, strerror(errno));
512 goto err_file;
513 }
514
515 scanner_init(&new_s, table->ctx, string, size, path, s->priv);
516
517 ok = parse(table, &new_s, include_depth + 1);
518 if (!ok)
519 goto err_unmap;
520
521 err_unmap:
522 unmap_file(string, size);
523 err_file:
524 fclose(file);
525 return ok;
526 }
527
528 static bool
parse(struct xkb_compose_table * table,struct scanner * s,unsigned include_depth)529 parse(struct xkb_compose_table *table, struct scanner *s,
530 unsigned include_depth)
531 {
532 enum rules_token tok;
533 union lvalue val;
534 struct keysym_from_name_cache *cache = s->priv;
535 xkb_keysym_t keysym;
536 struct production production;
537 enum { MAX_ERRORS = 10 };
538 int num_errors = 0;
539
540 initial:
541 production.len = 0;
542 production.has_keysym = false;
543 production.has_string = false;
544 production.mods = 0;
545 production.modmask = 0;
546
547 /* fallthrough */
548
549 initial_eol:
550 switch (tok = lex(s, &val)) {
551 case TOK_END_OF_LINE:
552 goto initial_eol;
553 case TOK_END_OF_FILE:
554 goto finished;
555 case TOK_INCLUDE:
556 goto include;
557 default:
558 goto lhs_tok;
559 }
560
561 include:
562 switch (tok = lex_include_string(s, table, &val)) {
563 case TOK_INCLUDE_STRING:
564 goto include_eol;
565 default:
566 goto unexpected;
567 }
568
569 include_eol:
570 switch (tok = lex(s, &val)) {
571 case TOK_END_OF_LINE:
572 if (!do_include(table, s, val.string.str, include_depth))
573 goto fail;
574 goto initial;
575 default:
576 goto unexpected;
577 }
578
579 lhs:
580 tok = lex(s, &val);
581 lhs_tok:
582 switch (tok) {
583 case TOK_COLON:
584 if (production.len <= 0) {
585 scanner_warn(s, "expected at least one keysym on left-hand side; skipping line");
586 goto skip;
587 }
588 goto rhs;
589 case TOK_IDENT:
590 if (!streq(val.string.str, "None")) {
591 scanner_err(s, "unrecognized identifier \"%s\"", val.string.str);
592 goto error;
593 }
594 production.mods = 0;
595 /* XXX Should only include the mods in resolve_mods(). */
596 production.modmask = 0xff;
597 goto lhs_keysym;
598 case TOK_BANG:
599 goto lhs_mod_list;
600 default:
601 goto lhs_keysym_tok;
602 }
603
604 lhs_keysym:
605 tok = lex(s, &val);
606 lhs_keysym_tok:
607 switch (tok) {
608 case TOK_LHS_KEYSYM:
609 keysym = cached_keysym_from_name(cache, val.string.str, val.string.len);
610 if (keysym == XKB_KEY_NoSymbol) {
611 scanner_err(s, "unrecognized keysym \"%s\" on left-hand side",
612 val.string.str);
613 goto error;
614 }
615 if (production.len + 1 > MAX_LHS_LEN) {
616 scanner_warn(s, "too many keysyms (%d) on left-hand side; skipping line",
617 MAX_LHS_LEN + 1);
618 goto skip;
619 }
620 production.lhs[production.len++] = keysym;
621 production.mods = 0;
622 production.modmask = 0;
623 goto lhs;
624 default:
625 goto unexpected;
626 }
627
628 lhs_mod_list: {
629 bool tilde = false;
630 xkb_mod_index_t mod;
631
632 tok = lex(s, &val);
633 if (tok == TOK_TILDE) {
634 tilde = true;
635 tok = lex(s, &val);
636 }
637
638 if (tok != TOK_IDENT) {
639 if (tilde || production.modmask == 0)
640 goto unexpected;
641 goto lhs_keysym_tok;
642 }
643
644 mod = resolve_modifier(val.string.str);
645 if (mod == XKB_MOD_INVALID) {
646 scanner_err(s, "unrecognized modifier \"%s\"",
647 val.string.str);
648 goto error;
649 }
650
651 production.modmask |= 1 << mod;
652 if (tilde)
653 production.mods &= ~(1 << mod);
654 else
655 production.mods |= 1 << mod;
656
657 goto lhs_mod_list;
658 }
659
660 rhs:
661 switch (tok = lex(s, &val)) {
662 case TOK_STRING:
663 if (production.has_string) {
664 scanner_warn(s, "right-hand side can have at most one string; skipping line");
665 goto skip;
666 }
667 if (val.string.len <= 0) {
668 scanner_warn(s, "right-hand side string must not be empty; skipping line");
669 goto skip;
670 }
671 if (val.string.len >= sizeof(production.string)) {
672 scanner_warn(s, "right-hand side string is too long; skipping line");
673 goto skip;
674 }
675 strcpy(production.string, val.string.str);
676 production.has_string = true;
677 goto rhs;
678 case TOK_IDENT:
679 keysym = cached_keysym_from_name(cache, val.string.str, val.string.len);
680 if (keysym == XKB_KEY_NoSymbol) {
681 scanner_err(s, "unrecognized keysym \"%s\" on right-hand side",
682 val.string.str);
683 goto error;
684 }
685 if (production.has_keysym) {
686 scanner_warn(s, "right-hand side can have at most one keysym; skipping line");
687 goto skip;
688 }
689 production.keysym = keysym;
690 production.has_keysym = true;
691 case TOK_END_OF_LINE:
692 if (!production.has_string && !production.has_keysym) {
693 scanner_warn(s, "right-hand side must have at least one of string or keysym; skipping line");
694 goto skip;
695 }
696 add_production(table, s, &production);
697 goto initial;
698 default:
699 goto unexpected;
700 }
701
702 unexpected:
703 if (tok != TOK_ERROR)
704 scanner_err(s, "unexpected token");
705 error:
706 num_errors++;
707 if (num_errors <= MAX_ERRORS)
708 goto skip;
709
710 scanner_err(s, "too many errors");
711 goto fail;
712
713 fail:
714 scanner_err(s, "failed to parse file");
715 return false;
716
717 skip:
718 while (tok != TOK_END_OF_LINE && tok != TOK_END_OF_FILE)
719 tok = lex(s, &val);
720 goto initial;
721
722 finished:
723 return true;
724 }
725
726 bool
parse_string(struct xkb_compose_table * table,const char * string,size_t len,const char * file_name)727 parse_string(struct xkb_compose_table *table, const char *string, size_t len,
728 const char *file_name)
729 {
730 struct scanner s;
731 struct keysym_from_name_cache cache;
732 memset(&cache, 0, sizeof(cache));
733 scanner_init(&s, table->ctx, string, len, file_name, &cache);
734 if (!parse(table, &s, 0))
735 return false;
736 /* Maybe the allocator can use the excess space. */
737 darray_shrink(table->nodes);
738 darray_shrink(table->utf8);
739 return true;
740 }
741
742 bool
parse_file(struct xkb_compose_table * table,FILE * file,const char * file_name)743 parse_file(struct xkb_compose_table *table, FILE *file, const char *file_name)
744 {
745 bool ok;
746 const char *string;
747 size_t size;
748
749 ok = map_file(file, &string, &size);
750 if (!ok) {
751 log_err(table->ctx, "Couldn't read Compose file %s: %s\n",
752 file_name, strerror(errno));
753 return false;
754 }
755
756 ok = parse_string(table, string, size, file_name);
757 unmap_file(string, size);
758 return ok;
759 }
760