xkbcomp: clean up compile_keymap function
[platform/upstream/libxkbcommon.git] / src / xkbcomp / xkbcomp.c
1 /*
2  * Copyright 2009  Dan Nicholson
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 shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20  *
21  * Except as contained in this notice, the names of the authors or their
22  * institutions shall not be used in advertising or otherwise to promote the
23  * sale, use or other dealings in this Software without prior written
24  * authorization from the authors.
25  */
26
27 #include "xkbcomp-priv.h"
28 #include "text.h"
29 #include "rules.h"
30
31 typedef bool (*compile_file_fn)(XkbFile *file,
32                                 struct xkb_keymap *keymap,
33                                 enum merge_mode merge);
34
35 static const compile_file_fn compile_file_fns[LAST_KEYMAP_FILE_TYPE + 1] = {
36     [FILE_TYPE_KEYCODES] = CompileKeycodes,
37     [FILE_TYPE_TYPES] = CompileKeyTypes,
38     [FILE_TYPE_COMPAT] = CompileCompatMap,
39     [FILE_TYPE_SYMBOLS] = CompileSymbols,
40 };
41
42 static struct xkb_keymap *
43 compile_keymap_file(struct xkb_context *ctx, XkbFile *file)
44 {
45     bool ok;
46     const char *main_name;
47     struct xkb_keymap *keymap;
48     XkbFile *files[LAST_KEYMAP_FILE_TYPE + 1] = { NULL };
49     enum xkb_file_type type;
50
51     keymap = xkb_map_new(ctx);
52     if (!keymap)
53         goto err;
54
55     main_name = file->name ? file->name : "(unnamed)";
56
57     if (file->file_type != FILE_TYPE_KEYMAP) {
58         log_err(ctx, "Cannot compile a %s file alone into a keymap\n",
59                 FileTypeText(file->file_type));
60         goto err;
61     }
62
63     /* Collect section files and check for duplicates. */
64     for (file = (XkbFile *) file->defs; file;
65          file = (XkbFile *) file->common.next) {
66         if (file->file_type < FIRST_KEYMAP_FILE_TYPE ||
67             file->file_type > LAST_KEYMAP_FILE_TYPE) {
68             log_err(ctx, "Cannot define %s in a keymap file\n",
69                     FileTypeText(file->file_type));
70             continue;
71         }
72
73         if (files[file->file_type]) {
74             log_err(ctx,
75                     "More than one %s section in keymap file; "
76                     "All sections after the first ignored\n",
77                     FileTypeText(file->file_type));
78             continue;
79         }
80
81         if (!file->topName) {
82             free(file->topName);
83             file->topName = strdup(main_name);
84         }
85
86         files[file->file_type] = file;
87     }
88
89     /*
90      * Check that all required section were provided.
91      * Report everything before failing.
92      */
93     ok = true;
94     for (type = FIRST_KEYMAP_FILE_TYPE;
95          type <= LAST_KEYMAP_FILE_TYPE;
96          type++) {
97         if (files[type] == NULL) {
98             log_err(ctx, "Required section %s missing from keymap\n",
99                     FileTypeText(type));
100             ok = false;
101         }
102     }
103     if (!ok)
104         goto err;
105
106     /* Compile sections. */
107     for (type = FIRST_KEYMAP_FILE_TYPE;
108          type <= LAST_KEYMAP_FILE_TYPE;
109          type++) {
110         log_dbg(ctx, "Compiling %s \"%s\"\n",
111                 FileTypeText(type), files[type]->topName);
112         ok = compile_file_fns[type](files[type], keymap, MERGE_OVERRIDE);
113         if (!ok) {
114             log_err(ctx, "Failed to compile %s\n", FileTypeText(type));
115             goto err;
116         }
117     }
118
119     ok = UpdateModifiersFromCompat(keymap);
120     if (!ok)
121         goto err;
122
123     return keymap;
124
125 err:
126     log_err(ctx, "Failed to compile keymap\n");
127     xkb_map_unref(keymap);
128     return NULL;
129 }
130
131 XKB_EXPORT struct xkb_keymap *
132 xkb_map_new_from_names(struct xkb_context *ctx,
133                        const struct xkb_rule_names *rmlvo_in,
134                        enum xkb_map_compile_flags flags)
135 {
136     bool ok;
137     struct xkb_component_names kccgst;
138     struct xkb_rule_names rmlvo = *rmlvo_in;
139     XkbFile *file;
140     struct xkb_keymap *keymap;
141
142     if (isempty(rmlvo.rules))
143         rmlvo.rules = DEFAULT_XKB_RULES;
144     if (isempty(rmlvo.model))
145         rmlvo.model = DEFAULT_XKB_MODEL;
146     if (isempty(rmlvo.layout))
147         rmlvo.layout = DEFAULT_XKB_LAYOUT;
148
149     ok = xkb_components_from_rules(ctx, &rmlvo, &kccgst);
150     if (!ok) {
151         log_err(ctx,
152                 "Couldn't look up rules '%s', model '%s', layout '%s', "
153                 "variant '%s', options '%s'\n",
154                 rmlvo.rules, rmlvo.model, rmlvo.layout, rmlvo.variant,
155                 rmlvo.options);
156         return NULL;
157     }
158
159     file = XkbFileFromComponents(ctx, &kccgst);
160
161     free(kccgst.keycodes);
162     free(kccgst.types);
163     free(kccgst.compat);
164     free(kccgst.symbols);
165
166     if (!file) {
167         log_err(ctx,
168                 "Failed to generate parsed XKB file from components\n");
169         return NULL;
170     }
171
172     keymap = compile_keymap_file(ctx, file);
173     FreeXkbFile(file);
174     return keymap;
175 }
176
177 XKB_EXPORT struct xkb_keymap *
178 xkb_map_new_from_string(struct xkb_context *ctx,
179                         const char *string,
180                         enum xkb_keymap_format format,
181                         enum xkb_map_compile_flags flags)
182 {
183     bool ok;
184     XkbFile *file;
185     struct xkb_keymap *keymap;
186
187     if (format != XKB_KEYMAP_FORMAT_TEXT_V1) {
188         log_err(ctx, "Unsupported keymap format %d\n", format);
189         return NULL;
190     }
191
192     if (!string) {
193         log_err(ctx, "No string specified to generate XKB keymap\n");
194         return NULL;
195     }
196
197     ok = XkbParseString(ctx, string, "input", &file);
198     if (!ok) {
199         log_err(ctx, "Failed to parse input xkb file\n");
200         return NULL;
201     }
202
203     keymap = compile_keymap_file(ctx, file);
204     FreeXkbFile(file);
205     return keymap;
206 }
207
208 XKB_EXPORT struct xkb_keymap *
209 xkb_map_new_from_file(struct xkb_context *ctx,
210                       FILE *file,
211                       enum xkb_keymap_format format,
212                       enum xkb_map_compile_flags flags)
213 {
214     bool ok;
215     XkbFile *xkb_file;
216     struct xkb_keymap *keymap;
217
218     if (format != XKB_KEYMAP_FORMAT_TEXT_V1) {
219         log_err(ctx, "Unsupported keymap format %d\n", format);
220         return NULL;
221     }
222
223     if (!file) {
224         log_err(ctx, "No file specified to generate XKB keymap\n");
225         return NULL;
226     }
227
228     ok = XkbParseFile(ctx, file, "(unknown file)", &xkb_file);
229     if (!ok) {
230         log_err(ctx, "Failed to parse input xkb file\n");
231         return NULL;
232     }
233
234     keymap = compile_keymap_file(ctx, xkb_file);
235     FreeXkbFile(xkb_file);
236     return keymap;
237 }