Messages: merge macros with and without message code
[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 "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
125 lex(struct scanner *s, union lvalue *val)
126 {
127 skip_more_whitespace_and_comments:
128     /* Skip spaces. */
129     while (is_space(scanner_peek(s)))
130         if (scanner_next(s) == '\n')
131             return TOK_END_OF_LINE;
132
133     /* Skip comments. */
134     if (scanner_chr(s, '#')) {
135         scanner_skip_to_eol(s);
136         goto skip_more_whitespace_and_comments;
137     }
138
139     /* See if we're done. */
140     if (scanner_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 (scanner_chr(s, '<')) {
149         while (scanner_peek(s) != '>' && !scanner_eol(s) && !scanner_eof(s))
150             scanner_buf_append(s, scanner_next(s));
151         if (!scanner_chr(s, '>')) {
152             scanner_err(s, "unterminated keysym literal");
153             return TOK_ERROR;
154         }
155         if (!scanner_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 (scanner_chr(s, ':'))
166         return TOK_COLON;
167     if (scanner_chr(s, '!'))
168         return TOK_BANG;
169     if (scanner_chr(s, '~'))
170         return TOK_TILDE;
171
172     /* String literal. */
173     if (scanner_chr(s, '\"')) {
174         while (!scanner_eof(s) && !scanner_eol(s) && scanner_peek(s) != '\"') {
175             if (scanner_chr(s, '\\')) {
176                 uint8_t o;
177                 if (scanner_chr(s, '\\')) {
178                     scanner_buf_append(s, '\\');
179                 }
180                 else if (scanner_chr(s, '"')) {
181                     scanner_buf_append(s, '"');
182                 }
183                 else if (scanner_chr(s, 'x') || scanner_chr(s, 'X')) {
184                     if (scanner_hex(s, &o))
185                         scanner_buf_append(s, (char) o);
186                     else
187                         scanner_warn(s, "illegal hexadecimal escape sequence in string literal");
188                 }
189                 else if (scanner_oct(s, &o)) {
190                     scanner_buf_append(s, (char) o);
191                 }
192                 else {
193                     scanner_warn(s, "unknown escape sequence (%c) in string literal", scanner_peek(s));
194                     /* Ignore. */
195                 }
196             } else {
197                 scanner_buf_append(s, scanner_next(s));
198             }
199         }
200         if (!scanner_chr(s, '\"')) {
201             scanner_err(s, "unterminated string literal");
202             return TOK_ERROR;
203         }
204         if (!scanner_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(scanner_peek(s)) || scanner_peek(s) == '_') {
219         s->buf_pos = 0;
220         while (is_alnum(scanner_peek(s)) || scanner_peek(s) == '_')
221             scanner_buf_append(s, scanner_next(s));
222         if (!scanner_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     scanner_skip_to_eol(s);
237
238     scanner_err(s, "unrecognized token");
239     return TOK_ERROR;
240 }
241
242 static enum rules_token
243 lex_include_string(struct scanner *s, struct xkb_compose_table *table,
244                    union lvalue *val_out)
245 {
246     while (is_space(scanner_peek(s)))
247         if (scanner_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 (!scanner_chr(s, '\"')) {
255         scanner_err(s, "include statement must be followed by a path");
256         return TOK_ERROR;
257     }
258
259     while (!scanner_eof(s) && !scanner_eol(s) && scanner_peek(s) != '\"') {
260         if (scanner_chr(s, '%')) {
261             if (scanner_chr(s, '%')) {
262                 scanner_buf_append(s, '%');
263             }
264             else if (scanner_chr(s, 'H')) {
265                 const char *home = xkb_context_getenv(table->ctx, "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 (!scanner_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 (scanner_chr(s, 'L')) {
276                 char *path = get_locale_compose_file_path(table->ctx, 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 (!scanner_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 (scanner_chr(s, 'S')) {
289                 const char *xlocaledir = get_xlocaledir_path(table->ctx);
290                 if (!scanner_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", scanner_peek(s));
297                 return TOK_ERROR;
298             }
299         } else {
300             scanner_buf_append(s, scanner_next(s));
301         }
302     }
303     if (!scanner_chr(s, '\"')) {
304         scanner_err(s, "unterminated include statement");
305         return TOK_ERROR;
306     }
307     if (!scanner_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
331 add_production(struct xkb_compose_table *table, struct scanner *s,
332                const struct production *production)
333 {
334     unsigned lhs_pos = 0;
335     uint32_t curr = darray_size(table->nodes) == 1 ? 0 : 1;
336     uint32_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 = 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
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
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
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
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
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,
731                 XKB_LOG_MESSAGE_NO_ID,
732                 "Couldn't read Compose file %s: %s\n",
733                 file_name, strerror(errno));
734         return false;
735     }
736
737     ok = parse_string(table, string, size, file_name);
738     unmap_file(string, size);
739     return ok;
740 }