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