Merge remote-tracking branch 'ran/fixes'
[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 <limits.h>
28 #include "xkbcomp.h"
29 #include "xkballoc.h"
30 #include "xkbrules.h"
31 #include "xkbpath.h"
32 #include "parseutils.h"
33 #include "utils.h"
34
35 /* Global debugging flags */
36 unsigned int debugFlags = 0;
37 unsigned int warningLevel = 0;
38
39 #define ISEMPTY(str) (!(str) || (strlen(str) == 0))
40
41 static XkbFile *
42 XkbKeymapFileFromComponents(const struct xkb_component_names * ktcsg)
43 {
44     XkbFile *keycodes, *types, *compat, *symbols, *geometry;
45     IncludeStmt *inc;
46
47     if (!ktcsg) {
48         ERROR("no components to generate keymap file from\n");
49         return NULL;
50     }
51
52     inc = IncludeCreate(ktcsg->keycodes, MergeDefault);
53     keycodes = CreateXKBFile(XkmKeyNamesIndex, NULL, (ParseCommon *)inc, 0);
54
55     inc = IncludeCreate(ktcsg->types, MergeDefault);
56     types = CreateXKBFile(XkmTypesIndex, NULL, (ParseCommon *)inc, 0);
57     AppendStmt(&keycodes->common, &types->common);
58
59     inc = IncludeCreate(ktcsg->compat, MergeDefault);
60     compat = CreateXKBFile(XkmCompatMapIndex, NULL, (ParseCommon *)inc, 0);
61     AppendStmt(&keycodes->common, &compat->common);
62
63     inc = IncludeCreate(ktcsg->symbols, MergeDefault);
64     symbols = CreateXKBFile(XkmSymbolsIndex, NULL, (ParseCommon *)inc, 0);
65     AppendStmt(&keycodes->common, &symbols->common);
66
67     inc = IncludeCreate(ktcsg->geometry, MergeDefault);
68     geometry = CreateXKBFile(XkmGeometryIndex, NULL, (ParseCommon *)inc, 0);
69     AppendStmt(&keycodes->common, &geometry->common);
70
71     return CreateXKBFile(XkmKeymapFile, ktcsg->keymap ? ktcsg->keymap : strdup(""),
72                          &keycodes->common, 0);
73 }
74
75 static struct xkb_component_names *
76 XkbComponentsFromRules(const char *rules, const XkbRF_VarDefsPtr defs)
77 {
78     FILE *rulesFile = NULL;
79     char *rulesPath = NULL;
80     static XkbRF_RulesPtr loaded = NULL;
81     static char *cached_name = NULL;
82     struct xkb_component_names * names = NULL;
83
84     if (!cached_name || strcmp(rules, cached_name) != 0) {
85         if (loaded)
86             XkbcRF_Free(loaded, True);
87         loaded = NULL;
88         free(cached_name);
89         cached_name = NULL;
90     }
91
92     if (!loaded) {
93         rulesFile = XkbFindFileInPath(rules, XkmRulesFile, &rulesPath);
94         if (!rulesFile) {
95             ERROR("could not find \"%s\" rules in XKB path\n", rules);
96             goto out;
97         }
98
99         if (!(loaded = _XkbTypedCalloc(1, XkbRF_RulesRec))) {
100             ERROR("failed to allocate XKB rules\n");
101             goto unwind_file;
102         }
103
104         if (!XkbcRF_LoadRules(rulesFile, loaded)) {
105             ERROR("failed to load XKB rules \"%s\"\n", rulesPath);
106             goto unwind_file;
107         }
108
109         cached_name = strdup(rules);
110     }
111
112     if (!(names = _XkbTypedCalloc(1, struct xkb_component_names))) {
113         ERROR("failed to allocate XKB components\n");
114         goto unwind_file;
115     }
116
117     if (!XkbcRF_GetComponents(loaded, defs, names)) {
118         free(names->keymap);
119         free(names->keycodes);
120         free(names->types);
121         free(names->compat);
122         free(names->symbols);
123         free(names->geometry);
124         free(names);
125         names = NULL;
126         ERROR("no components returned from XKB rules \"%s\"\n", rulesPath);
127     }
128
129 unwind_file:
130     if (rulesFile)
131         fclose(rulesFile);
132     free(rulesPath);
133 out:
134     return names;
135 }
136
137 struct xkb_desc *
138 xkb_compile_keymap_from_rules(const struct xkb_rule_names *rmlvo)
139 {
140     XkbRF_VarDefsRec defs;
141     struct xkb_component_names * names;
142     struct xkb_desc * xkb;
143
144     if (!rmlvo || ISEMPTY(rmlvo->rules) || ISEMPTY(rmlvo->layout)) {
145         ERROR("rules and layout required to generate XKB keymap\n");
146         return NULL;
147     }
148
149     defs.model = rmlvo->model;
150     defs.layout = rmlvo->layout;
151     defs.variant = rmlvo->variant;
152     defs.options = rmlvo->options;
153
154     names = XkbComponentsFromRules(rmlvo->rules, &defs);
155     if (!names) {
156         ERROR("failed to generate XKB components from rules \"%s\"\n",
157               rmlvo->rules);
158         return NULL;
159     }
160
161     xkb = xkb_compile_keymap_from_components(names);
162
163     free(names->keymap);
164     free(names->keycodes);
165     free(names->types);
166     free(names->compat);
167     free(names->symbols);
168     free(names->geometry);
169     free(names);
170
171     return xkb;
172 }
173
174 static XkbFile *
175 XkbChooseMap(XkbFile *file, const char *name)
176 {
177     XkbFile *map = file;
178
179     /* map specified? */
180     if (name) {
181         while (map) {
182             if (map->name && strcmp(map->name, name) == 0)
183                 break;
184             map = (XkbFile *) map->common.next;
185         }
186
187         if (!map)
188             ERROR("no map named \"%s\" in input file\n", name);
189     }
190     else if (file->common.next) {
191         /* look for map with XkbLC_Default flag. */
192         for (; map; map = (XkbFile *) map->common.next) {
193             if (map->flags & XkbLC_Default)
194                 break;
195         }
196
197         if (!map) {
198             map = file;
199             WARN("no map specified, but components have several\n");
200             WARN("using the first defined map, \"%s\"\n",
201                  map->name ? map->name : "");
202         }
203     }
204
205     return map;
206 }
207
208 struct xkb_desc *
209 xkb_compile_keymap_from_components(const struct xkb_component_names * ktcsg)
210 {
211     XkbFile *file, *mapToUse;
212     struct xkb_desc * xkb;
213
214     uSetErrorFile(NULL);
215
216     if (!ktcsg || ISEMPTY(ktcsg->keycodes)) {
217         ERROR("keycodes required to generate XKB keymap\n");
218         goto fail;
219     }
220
221     if (!(file = XkbKeymapFileFromComponents(ktcsg))) {
222         ERROR("failed to generate parsed XKB file from components\n");
223         goto fail;
224     }
225
226     /* Find map to use */
227     if (!(mapToUse = XkbChooseMap(file, NULL)))
228         goto unwind_file;
229
230     /* Compile the keyboard */
231     if (!(xkb = XkbcAllocKeyboard())) {
232         ERROR("could not allocate keyboard description\n");
233         goto unwind_file;
234     }
235
236     if (!CompileKeymap(mapToUse, xkb, MergeReplace)) {
237         ERROR("failed to compile keymap\n");
238         goto unwind_xkb;
239     }
240
241     return xkb;
242 unwind_xkb:
243     XkbcFreeKeyboard(xkb, XkbAllComponentsMask, True);
244 unwind_file:
245     /* XXX: here's where we would free the XkbFile */
246 fail:
247     return NULL;
248 }
249
250 static struct xkb_desc *
251 compile_keymap(XkbFile *file, const char *mapName)
252 {
253     XkbFile *mapToUse;
254     struct xkb_desc * xkb;
255
256     /* Find map to use */
257     if (!(mapToUse = XkbChooseMap(file, mapName)))
258         goto unwind_file;
259
260     switch (mapToUse->type) {
261     case XkmSemanticsFile:
262     case XkmLayoutFile:
263     case XkmKeymapFile:
264         break;
265     default:
266         ERROR("file type %d not handled\n", mapToUse->type);
267         goto unwind_file;
268     }
269
270     /* Compile the keyboard */
271     if (!(xkb = XkbcAllocKeyboard())) {
272         ERROR("could not allocate keyboard description\n");
273         goto unwind_file;
274     }
275
276     if (!CompileKeymap(mapToUse, xkb, MergeReplace)) {
277         ERROR("failed to compile keymap\n");
278         goto unwind_xkb;
279     }
280
281     return xkb;
282 unwind_xkb:
283     XkbcFreeKeyboard(xkb, XkbAllComponentsMask, True);
284 unwind_file:
285     /* XXX: here's where we would free the XkbFile */
286
287     return NULL;
288 }
289
290 struct xkb_desc *
291 xkb_compile_keymap_from_string(const char *string, const char *mapName)
292 {
293     XkbFile *file;
294
295     if (!string) {
296         ERROR("no string specified to generate XKB keymap\n");
297         return NULL;
298     }
299
300     setScanState("input", 1);
301     if (!XKBParseString(string, &file) || !file) {
302         ERROR("failed to parse input xkb file\n");
303         return NULL;
304     }
305
306     return compile_keymap(file, mapName);
307 }
308
309 struct xkb_desc *
310 xkb_compile_keymap_from_file(FILE *inputFile, const char *mapName)
311 {
312     XkbFile *file;
313
314     if (!inputFile) {
315         ERROR("no file specified to generate XKB keymap\n");
316         return NULL;
317     }
318
319     setScanState("input", 1);
320     if (!XKBParseFile(inputFile, &file) || !file) {
321         ERROR("failed to parse input xkb file\n");
322         return NULL;
323     }
324
325     return compile_keymap(file, mapName);
326 }
327
328 void
329 xkb_free_keymap(struct xkb_desc *xkb)
330 {
331        XkbcFreeKeyboard(xkb, 0, True);
332 }