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