1 /*
2 * Copyright © 2013 Ran Benita <[email protected]>
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 "config.h"
56
57 #include <errno.h>
58
59 #include "utils.h"
60 #include "scanner-utils.h"
61 #include "table.h"
62 #include "paths.h"
63 #include "utf8.h"
64 #include "parser.h"
65
66 #define MAX_LHS_LEN 10
67 #define MAX_INCLUDE_DEPTH 5
68
69 /*
70 * Grammar adapted from libX11/modules/im/ximcp/imLcPrs.c.
71 * See also the XCompose(5) manpage.
72 *
73 * FILE ::= { [PRODUCTION] [COMMENT] "\n" | INCLUDE }
74 * INCLUDE ::= "include" '"' INCLUDE_STRING '"'
75 * PRODUCTION ::= LHS ":" RHS [ COMMENT ]
76 * COMMENT ::= "#" {<any character except null or newline>}
77 * LHS ::= EVENT { EVENT }
78 * EVENT ::= [MODIFIER_LIST] "<" keysym ">"
79 * MODIFIER_LIST ::= (["!"] {MODIFIER} ) | "None"
80 * MODIFIER ::= ["~"] MODIFIER_NAME
81 * MODIFIER_NAME ::= ("Ctrl"|"Lock"|"Caps"|"Shift"|"Alt"|"Meta")
82 * RHS ::= ( STRING | keysym | STRING keysym )
83 * STRING ::= '"' { CHAR } '"'
84 * CHAR ::= GRAPHIC_CHAR | ESCAPED_CHAR
85 * GRAPHIC_CHAR ::= locale (codeset) dependent code
86 * ESCAPED_CHAR ::= ('\\' | '\"' | OCTAL | HEX )
87 * OCTAL ::= '\' OCTAL_CHAR [OCTAL_CHAR [OCTAL_CHAR]]
88 * OCTAL_CHAR ::= (0|1|2|3|4|5|6|7)
89 * HEX ::= '\' (x|X) HEX_CHAR [HEX_CHAR]]
90 * HEX_CHAR ::= (0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|a|b|c|d|e|f)
91 *
92 * INCLUDE_STRING is a filesystem path, with the following %-expansions:
93 * %% - '%'.
94 * %H - The user's home directory (the $HOME environment variable).
95 * %L - The name of the locale specific Compose file (e.g.,
96 * "/usr/share/X11/locale/<localename>/Compose").
97 * %S - The name of the system directory for Compose files (e.g.,
98 * "/usr/share/X11/locale").
99 */
100
101 enum rules_token {
102 TOK_END_OF_FILE = 0,
103 TOK_END_OF_LINE,
104 TOK_INCLUDE,
105 TOK_INCLUDE_STRING,
106 TOK_LHS_KEYSYM,
107 TOK_COLON,
108 TOK_BANG,
109 TOK_TILDE,
110 TOK_STRING,
111 TOK_IDENT,
112 TOK_ERROR
113 };
114
115 /* Values returned with some tokens, like yylval. */
116 union lvalue {
117 struct {
118 /* Still \0-terminated. */
119 const char *str;
120 size_t len;
121 } string;
122 };
123
124 static enum rules_token
lex(struct scanner * s,union lvalue * val)125 lex(struct scanner *s, union lvalue *val)
126 {
127 skip_more_whitespace_and_comments:
128 /* Skip spaces. */
129 while (is_space(peek(s)))
130 if (next(s) == '\n')
131 return TOK_END_OF_LINE;
132
133 /* Skip comments. */
134 if (chr(s, '#')) {
135 skip_to_eol(s);
136 goto skip_more_whitespace_and_comments;
137 }
138
139 /* See if we're done. */
140 if (eof(s)) return TOK_END_OF_FILE;
141
142 /* New token. */
143 s->token_line = s->line;
144 s->token_column = s->column;
145 s->buf_pos = 0;
146
147 /* LHS Keysym. */
148 if (chr(s, '<')) {
149 while (peek(s) != '>' && !eol(s) && !eof(s))
150 buf_append(s, next(s));
151 if (!chr(s, '>')) {
152 scanner_err(s, "unterminated keysym literal");
153 return TOK_ERROR;
154 }
155 if (!buf_append(s, '\0')) {
156 scanner_err(s, "keysym literal is too long");
157 return TOK_ERROR;
158 }
159 val->string.str = s->buf;
160 val->string.len = s->buf_pos;
161 return TOK_LHS_KEYSYM;
162 }
163
164 /* Colon. */
165 if (chr(s, ':'))
166 return TOK_COLON;
167 if (chr(s, '!'))
168 return TOK_BANG;
169 if (chr(s, '~'))
170 return TOK_TILDE;
171
172 /* String literal. */
173 if (chr(s, '\"')) {
174 while (!eof(s) && !eol(s) && peek(s) != '\"') {
175 if (chr(s, '\\')) {
176 uint8_t o;
177 if (chr(s, '\\')) {
178 buf_append(s, '\\');
179 }
180 else if (chr(s, '"')) {
181 buf_append(s, '"');
182 }
183 else if (chr(s, 'x') || chr(s, 'X')) {
184 if (hex(s, &o))
185 buf_append(s, (char) o);
186 else
187 scanner_warn(s, "illegal hexadecimal escape sequence in string literal");
188 }
189 else if (oct(s, &o)) {
190 buf_append(s, (char) o);
191 }
192 else {
193 scanner_warn(s, "unknown escape sequence (%c) in string literal", peek(s));
194 /* Ignore. */
195 }
196 } else {
197 buf_append(s, next(s));
198 }
199 }
200 if (!chr(s, '\"')) {
201 scanner_err(s, "unterminated string literal");
202 return TOK_ERROR;
203 }
204 if (!buf_append(s, '\0')) {
205 scanner_err(s, "string literal is too long");
206 return TOK_ERROR;
207 }
208 if (!is_valid_utf8(s->buf, s->buf_pos - 1)) {
209 scanner_err(s, "string literal is not a valid UTF-8 string");
210 return TOK_ERROR;
211 }
212 val->string.str = s->buf;
213 val->string.len = s->buf_pos;
214 return TOK_STRING;
215 }
216
217 /* Identifier or include. */
218 if (is_alpha(peek(s)) || peek(s) == '_') {
219 s->buf_pos = 0;
220 while (is_alnum(peek(s)) || peek(s) == '_')
221 buf_append(s, next(s));
222 if (!buf_append(s, '\0')) {
223 scanner_err(s, "identifier is too long");
224 return TOK_ERROR;
225 }
226
227 if (streq(s->buf, "include"))
228 return TOK_INCLUDE;
229
230 val->string.str = s->buf;
231 val->string.len = s->buf_pos;
232 return TOK_IDENT;
233 }
234
235 /* Discard rest of line. */
236 skip_to_eol(s);
237
238 scanner_err(s, "unrecognized token");
239 return TOK_ERROR;
240 }
241
242 static enum rules_token
lex_include_string(struct scanner * s,struct xkb_compose_table * table,union lvalue * val_out)243 lex_include_string(struct scanner *s, struct xkb_compose_table *table,
244 union lvalue *val_out)
245 {
246 while (is_space(peek(s)))
247 if (next(s) == '\n')
248 return TOK_END_OF_LINE;
249
250 s->token_line = s->line;
251 s->token_column = s->column;
252 s->buf_pos = 0;
253
254 if (!chr(s, '\"')) {
255 scanner_err(s, "include statement must be followed by a path");
256 return TOK_ERROR;
257 }
258
259 while (!eof(s) && !eol(s) && peek(s) != '\"') {
260 if (chr(s, '%')) {
261 if (chr(s, '%')) {
262 buf_append(s, '%');
263 }
264 else if (chr(s, 'H')) {
265 const char *home = secure_getenv("HOME");
266 if (!home) {
267 scanner_err(s, "%%H was used in an include statement, but the HOME environment variable is not set");
268 return TOK_ERROR;
269 }
270 if (!buf_appends(s, home)) {
271 scanner_err(s, "include path after expanding %%H is too long");
272 return TOK_ERROR;
273 }
274 }
275 else if (chr(s, 'L')) {
276 char *path = get_locale_compose_file_path(table->locale);
277 if (!path) {
278 scanner_err(s, "failed to expand %%L to the locale Compose file");
279 return TOK_ERROR;
280 }
281 if (!buf_appends(s, path)) {
282 free(path);
283 scanner_err(s, "include path after expanding %%L is too long");
284 return TOK_ERROR;
285 }
286 free(path);
287 }
288 else if (chr(s, 'S')) {
289 const char *xlocaledir = get_xlocaledir_path();
290 if (!buf_appends(s, xlocaledir)) {
291 scanner_err(s, "include path after expanding %%S is too long");
292 return TOK_ERROR;
293 }
294 }
295 else {
296 scanner_err(s, "unknown %% format (%c) in include statement", peek(s));
297 return TOK_ERROR;
298 }
299 } else {
300 buf_append(s, next(s));
301 }
302 }
303 if (!chr(s, '\"')) {
304 scanner_err(s, "unterminated include statement");
305 return TOK_ERROR;
306 }
307 if (!buf_append(s, '\0')) {
308 scanner_err(s, "include path is too long");
309 return TOK_ERROR;
310 }
311 val_out->string.str = s->buf;
312 val_out->string.len = s->buf_pos;
313 return TOK_INCLUDE_STRING;
314 }
315
316 struct production {
317 xkb_keysym_t lhs[MAX_LHS_LEN];
318 unsigned int len;
319 xkb_keysym_t keysym;
320 char string[256];
321 /* At least one of these is true. */
322 bool has_keysym;
323 bool has_string;
324
325 /* The matching is as follows: (active_mods & modmask) == mods. */
326 xkb_mod_mask_t modmask;
327 xkb_mod_mask_t mods;
328 };
329
330 static void
add_production(struct xkb_compose_table * table,struct scanner * s,const struct production * production)331 add_production(struct xkb_compose_table *table, struct scanner *s,
332 const struct production *production)
333 {
334 unsigned lhs_pos = 0;
335 uint16_t curr = darray_size(table->nodes) == 1 ? 0 : 1;
336 uint16_t *pptr = NULL;
337 struct compose_node *node = NULL;
338
339 /* Warn before potentially going over the limit, discard silently after. */
340 if (darray_size(table->nodes) + production->len + MAX_LHS_LEN > MAX_COMPOSE_NODES)
341 scanner_warn(s, "too many sequences for one Compose file; will ignore further lines");
342 if (darray_size(table->nodes) + production->len >= MAX_COMPOSE_NODES)
343 return;
344
345 /*
346 * Insert the sequence to the ternary search tree, creating new nodes as
347 * needed.
348 *
349 * TODO: We insert in the order given, this means some inputs can create
350 * long O(n) chains, which results in total O(n^2) parsing time. We should
351 * ensure the tree is reasonably balanced somehow.
352 */
353 while (true) {
354 const xkb_keysym_t keysym = production->lhs[lhs_pos];
355 const bool last = lhs_pos + 1 == production->len;
356
357 if (curr == 0) {
358 /*
359 * Create a new node and update the parent pointer to it.
360 * Update the pointer first because the append invalidates it.
361 */
362 struct compose_node new = {
363 .keysym = keysym,
364 .lokid = 0,
365 .hikid = 0,
366 .internal = {
367 .eqkid = 0,
368 .is_leaf = false,
369 },
370 };
371 curr = darray_size(table->nodes);
372 if (pptr != NULL) {
373 *pptr = curr;
374 pptr = NULL;
375 }
376 darray_append(table->nodes, new);
377 }
378
379 node = &darray_item(table->nodes, curr);
380
381 if (keysym < node->keysym) {
382 pptr = &node->lokid;
383 curr = node->lokid;
384 } else if (keysym > node->keysym) {
385 pptr = &node->hikid;
386 curr = node->hikid;
387 } else if (!last) {
388 if (node->is_leaf) {
389 scanner_warn(s, "a sequence already exists which is a prefix of this sequence; overriding");
390 node->internal.eqkid = node->lokid = node->hikid = 0;
391 node->internal.is_leaf = false;
392 }
393 lhs_pos++;
394 pptr = &node->internal.eqkid;
395 curr = node->internal.eqkid;
396 } else {
397 if (node->is_leaf) {
398 bool same_string =
399 (node->leaf.utf8 == 0 && !production->has_string) ||
400 (
401 node->leaf.utf8 != 0 && production->has_string &&
402 streq(&darray_item(table->utf8, node->leaf.utf8),
403 production->string)
404 );
405 bool same_keysym =
406 (node->leaf.keysym == XKB_KEY_NoSymbol && !production->has_keysym) ||
407 (
408 node->leaf.keysym != XKB_KEY_NoSymbol && production->has_keysym &&
409 node->leaf.keysym == production->keysym
410 );
411 if (same_string && same_keysym) {
412 scanner_warn(s, "this compose sequence is a duplicate of another; skipping line");
413 return;
414 } else {
415 scanner_warn(s, "this compose sequence already exists; overriding");
416 }
417 } else if (node->internal.eqkid != 0) {
418 scanner_warn(s, "this compose sequence is a prefix of another; skipping line");
419 return;
420 }
421 node->is_leaf = true;
422 if (production->has_string) {
423 node->leaf.utf8 = darray_size(table->utf8);
424 darray_append_items(table->utf8, production->string,
425 strlen(production->string) + 1);
426 }
427 if (production->has_keysym) {
428 node->leaf.keysym = production->keysym;
429 }
430 return;
431 }
432 }
433 }
434
435 /* Should match resolve_modifier(). */
436 #define ALL_MODS_MASK ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3))
437
438 static xkb_mod_index_t
resolve_modifier(const char * name)439 resolve_modifier(const char *name)
440 {
441 static const struct {
442 const char *name;
443 xkb_mod_index_t mod;
444 } mods[] = {
445 { "Shift", 0 },
446 { "Ctrl", 2 },
447 { "Alt", 3 },
448 { "Meta", 3 },
449 { "Lock", 1 },
450 { "Caps", 1 },
451 };
452
453 for (unsigned i = 0; i < ARRAY_SIZE(mods); i++)
454 if (streq(name, mods[i].name))
455 return mods[i].mod;
456
457 return XKB_MOD_INVALID;
458 }
459
460 static bool
461 parse(struct xkb_compose_table *table, struct scanner *s,
462 unsigned include_depth);
463
464 static bool
do_include(struct xkb_compose_table * table,struct scanner * s,const char * path,unsigned include_depth)465 do_include(struct xkb_compose_table *table, struct scanner *s,
466 const char *path, unsigned include_depth)
467 {
468 FILE *file;
469 bool ok;
470 char *string;
471 size_t size;
472 struct scanner new_s;
473
474 if (include_depth >= MAX_INCLUDE_DEPTH) {
475 scanner_err(s, "maximum include depth (%d) exceeded; maybe there is an include loop?",
476 MAX_INCLUDE_DEPTH);
477 return false;
478 }
479
480 file = fopen(path, "rb");
481 if (!file) {
482 scanner_err(s, "failed to open included Compose file \"%s\": %s",
483 path, strerror(errno));
484 return false;
485 }
486
487 ok = map_file(file, &string, &size);
488 if (!ok) {
489 scanner_err(s, "failed to read included Compose file \"%s\": %s",
490 path, strerror(errno));
491 goto err_file;
492 }
493
494 scanner_init(&new_s, table->ctx, string, size, path, s->priv);
495
496 ok = parse(table, &new_s, include_depth + 1);
497 if (!ok)
498 goto err_unmap;
499
500 err_unmap:
501 unmap_file(string, size);
502 err_file:
503 fclose(file);
504 return ok;
505 }
506
507 static bool
parse(struct xkb_compose_table * table,struct scanner * s,unsigned include_depth)508 parse(struct xkb_compose_table *table, struct scanner *s,
509 unsigned include_depth)
510 {
511 enum rules_token tok;
512 union lvalue val;
513 xkb_keysym_t keysym;
514 struct production production;
515 enum { MAX_ERRORS = 10 };
516 int num_errors = 0;
517
518 initial:
519 production.len = 0;
520 production.has_keysym = false;
521 production.has_string = false;
522 production.mods = 0;
523 production.modmask = 0;
524
525 /* fallthrough */
526
527 initial_eol:
528 switch (tok = lex(s, &val)) {
529 case TOK_END_OF_LINE:
530 goto initial_eol;
531 case TOK_END_OF_FILE:
532 goto finished;
533 case TOK_INCLUDE:
534 goto include;
535 default:
536 goto lhs_tok;
537 }
538
539 include:
540 switch (tok = lex_include_string(s, table, &val)) {
541 case TOK_INCLUDE_STRING:
542 goto include_eol;
543 default:
544 goto unexpected;
545 }
546
547 include_eol:
548 switch (tok = lex(s, &val)) {
549 case TOK_END_OF_LINE:
550 if (!do_include(table, s, val.string.str, include_depth))
551 goto fail;
552 goto initial;
553 default:
554 goto unexpected;
555 }
556
557 lhs:
558 tok = lex(s, &val);
559 lhs_tok:
560 switch (tok) {
561 case TOK_COLON:
562 if (production.len <= 0) {
563 scanner_warn(s, "expected at least one keysym on left-hand side; skipping line");
564 goto skip;
565 }
566 goto rhs;
567 case TOK_IDENT:
568 if (streq(val.string.str, "None")) {
569 production.mods = 0;
570 production.modmask = ALL_MODS_MASK;
571 goto lhs_keysym;
572 }
573 goto lhs_mod_list_tok;
574 case TOK_TILDE:
575 goto lhs_mod_list_tok;
576 case TOK_BANG:
577 production.modmask = ALL_MODS_MASK;
578 goto lhs_mod_list;
579 default:
580 goto lhs_keysym_tok;
581 }
582
583 lhs_keysym:
584 tok = lex(s, &val);
585 lhs_keysym_tok:
586 switch (tok) {
587 case TOK_LHS_KEYSYM:
588 keysym = xkb_keysym_from_name(val.string.str, XKB_KEYSYM_NO_FLAGS);
589 if (keysym == XKB_KEY_NoSymbol) {
590 scanner_err(s, "unrecognized keysym \"%s\" on left-hand side",
591 val.string.str);
592 goto error;
593 }
594 if (production.len + 1 > MAX_LHS_LEN) {
595 scanner_warn(s, "too many keysyms (%d) on left-hand side; skipping line",
596 MAX_LHS_LEN + 1);
597 goto skip;
598 }
599 production.lhs[production.len++] = keysym;
600 production.mods = 0;
601 production.modmask = 0;
602 goto lhs;
603 default:
604 goto unexpected;
605 }
606
607 lhs_mod_list:
608 tok = lex(s, &val);
609 lhs_mod_list_tok: {
610 bool tilde = false;
611 xkb_mod_index_t mod;
612
613 if (tok != TOK_TILDE && tok != TOK_IDENT)
614 goto lhs_keysym_tok;
615
616 if (tok == TOK_TILDE) {
617 tilde = true;
618 tok = lex(s, &val);
619 }
620
621 if (tok != TOK_IDENT)
622 goto unexpected;
623
624 mod = resolve_modifier(val.string.str);
625 if (mod == XKB_MOD_INVALID) {
626 scanner_err(s, "unrecognized modifier \"%s\"",
627 val.string.str);
628 goto error;
629 }
630
631 production.modmask |= 1 << mod;
632 if (tilde)
633 production.mods &= ~(1 << mod);
634 else
635 production.mods |= 1 << mod;
636
637 goto lhs_mod_list;
638 }
639
640 rhs:
641 switch (tok = lex(s, &val)) {
642 case TOK_STRING:
643 if (production.has_string) {
644 scanner_warn(s, "right-hand side can have at most one string; skipping line");
645 goto skip;
646 }
647 if (val.string.len <= 0) {
648 scanner_warn(s, "right-hand side string must not be empty; skipping line");
649 goto skip;
650 }
651 if (val.string.len >= sizeof(production.string)) {
652 scanner_warn(s, "right-hand side string is too long; skipping line");
653 goto skip;
654 }
655 strcpy(production.string, val.string.str);
656 production.has_string = true;
657 goto rhs;
658 case TOK_IDENT:
659 keysym = xkb_keysym_from_name(val.string.str, XKB_KEYSYM_NO_FLAGS);
660 if (keysym == XKB_KEY_NoSymbol) {
661 scanner_err(s, "unrecognized keysym \"%s\" on right-hand side",
662 val.string.str);
663 goto error;
664 }
665 if (production.has_keysym) {
666 scanner_warn(s, "right-hand side can have at most one keysym; skipping line");
667 goto skip;
668 }
669 production.keysym = keysym;
670 production.has_keysym = true;
671 /* fallthrough */
672 case TOK_END_OF_LINE:
673 if (!production.has_string && !production.has_keysym) {
674 scanner_warn(s, "right-hand side must have at least one of string or keysym; skipping line");
675 goto skip;
676 }
677 add_production(table, s, &production);
678 goto initial;
679 default:
680 goto unexpected;
681 }
682
683 unexpected:
684 if (tok != TOK_ERROR)
685 scanner_err(s, "unexpected token");
686 error:
687 num_errors++;
688 if (num_errors <= MAX_ERRORS)
689 goto skip;
690
691 scanner_err(s, "too many errors");
692 goto fail;
693
694 fail:
695 scanner_err(s, "failed to parse file");
696 return false;
697
698 skip:
699 while (tok != TOK_END_OF_LINE && tok != TOK_END_OF_FILE)
700 tok = lex(s, &val);
701 goto initial;
702
703 finished:
704 return true;
705 }
706
707 bool
parse_string(struct xkb_compose_table * table,const char * string,size_t len,const char * file_name)708 parse_string(struct xkb_compose_table *table, const char *string, size_t len,
709 const char *file_name)
710 {
711 struct scanner s;
712 scanner_init(&s, table->ctx, string, len, file_name, NULL);
713 if (!parse(table, &s, 0))
714 return false;
715 /* Maybe the allocator can use the excess space. */
716 darray_shrink(table->nodes);
717 darray_shrink(table->utf8);
718 return true;
719 }
720
721 bool
parse_file(struct xkb_compose_table * table,FILE * file,const char * file_name)722 parse_file(struct xkb_compose_table *table, FILE *file, const char *file_name)
723 {
724 bool ok;
725 char *string;
726 size_t size;
727
728 ok = map_file(file, &string, &size);
729 if (!ok) {
730 log_err(table->ctx, "Couldn't read Compose file %s: %s\n",
731 file_name, strerror(errno));
732 return false;
733 }
734
735 ok = parse_string(table, string, size, file_name);
736 unmap_file(string, size);
737 return ok;
738 }
739