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
29Permission to use, copy, modify, distribute, and sell this software
30and its documentation for any purpose is hereby granted without fee,
31provided that the above copyright notice appear in all copies and
32that both that copyright notice and this permission notice appear
33in supporting documentation, and that the name of Oki Technosystems
34Laboratory and Fuji Xerox not be used in advertising or publicity
35pertaining to distribution of the software without specific, written
36prior permission.
37Oki Technosystems Laboratory and Fuji Xerox make no representations
38about the suitability of this software for any purpose.  It is provided
39"as is" without express or implied warranty.
40
41OKI TECHNOSYSTEMS LABORATORY AND FUJI XEROX DISCLAIM ALL WARRANTIES
42WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
43MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OKI TECHNOSYSTEMS
44LABORATORY AND FUJI XEROX BE LIABLE FOR ANY SPECIAL, INDIRECT OR
45CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
46OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
47OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
48OR 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 */
75struct 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
84static xkb_keysym_t
85cached_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
137enum 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. */
152union lvalue {
153    struct {
154        /* Still \0-terminated. */
155        const char *str;
156        size_t len;
157    } string;
158};
159
160static enum rules_token
161lex(struct scanner *s, union lvalue *val)
162{
163skip_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
278static enum rules_token
279lex_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
352struct 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
364static uint32_t
365add_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
376static void
377add_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
459static xkb_mod_index_t
460resolve_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
481static bool
482parse(struct xkb_compose_table *table, struct scanner *s,
483      unsigned include_depth);
484
485static bool
486do_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
521err_unmap:
522    unmap_file(string, size);
523err_file:
524    fclose(file);
525    return ok;
526}
527
528static bool
529parse(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
540initial:
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
549initial_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
561include:
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
569include_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
579lhs:
580    tok = lex(s, &val);
581lhs_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
604lhs_keysym:
605    tok = lex(s, &val);
606lhs_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
628lhs_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
660rhs:
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
702unexpected:
703    if (tok != TOK_ERROR)
704        scanner_err(s, "unexpected token");
705error:
706    num_errors++;
707    if (num_errors <= MAX_ERRORS)
708        goto skip;
709
710    scanner_err(s, "too many errors");
711    goto fail;
712
713fail:
714    scanner_err(s, "failed to parse file");
715    return false;
716
717skip:
718    while (tok != TOK_END_OF_LINE && tok != TOK_END_OF_FILE)
719        tok = lex(s, &val);
720    goto initial;
721
722finished:
723    return true;
724}
725
726bool
727parse_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
742bool
743parse_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