Imported Upstream version 1.15.1
[deb_xorg-server.git] / xkb / ddxLoad.c
CommitLineData
a09e091a
JB
1/************************************************************
2Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc.
3
4Permission to use, copy, modify, and distribute this
5software and its documentation for any purpose and without
6fee is hereby granted, provided that the above copyright
7notice appear in all copies and that both that copyright
8notice and this permission notice appear in supporting
9documentation, and that the name of Silicon Graphics not be
10used in advertising or publicity pertaining to distribution
11of the software without specific prior written permission.
12Silicon Graphics makes no representation about the suitability
13of this software for any purpose. It is provided "as is"
14without any express or implied warranty.
15
16SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
23THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25********************************************************/
26
27#ifdef HAVE_DIX_CONFIG_H
28#include <dix-config.h>
29#endif
30
31#include <xkb-config.h>
32
33#include <stdio.h>
34#include <ctype.h>
35#include <X11/X.h>
36#include <X11/Xos.h>
37#include <X11/Xproto.h>
38#include <X11/keysym.h>
39#include <X11/extensions/XKM.h>
40#include "inputstr.h"
41#include "scrnintstr.h"
42#include "windowstr.h"
43#define XKBSRV_NEED_FILE_FUNCS
44#include <xkbsrv.h>
45#include <X11/extensions/XI.h>
46#include "xkb.h"
47
48 /*
49 * If XKM_OUTPUT_DIR specifies a path without a leading slash, it is
50 * relative to the top-level XKB configuration directory.
51 * Making the server write to a subdirectory of that directory
52 * requires some work in the general case (install procedure
53 * has to create links to /var or somesuch on many machines),
54 * so we just compile into /usr/tmp for now.
55 */
56#ifndef XKM_OUTPUT_DIR
57#define XKM_OUTPUT_DIR "compiled/"
58#endif
59
60#define PRE_ERROR_MSG "\"The XKEYBOARD keymap compiler (xkbcomp) reports:\""
61#define ERROR_PREFIX "\"> \""
62#define POST_ERROR_MSG1 "\"Errors from xkbcomp are not fatal to the X server\""
63#define POST_ERROR_MSG2 "\"End of messages from xkbcomp\""
64
65#if defined(WIN32)
66#define PATHSEPARATOR "\\"
67#else
68#define PATHSEPARATOR "/"
69#endif
70
71static void
72OutputDirectory(char *outdir, size_t size)
73{
74#ifndef WIN32
75 /* Can we write an xkm and then open it too? */
76 if (access(XKM_OUTPUT_DIR, W_OK | X_OK) == 0 &&
77 (strlen(XKM_OUTPUT_DIR) < size)) {
78 (void) strcpy(outdir, XKM_OUTPUT_DIR);
79 }
80 else
81#else
82 if (strlen(Win32TempDir()) + 1 < size) {
83 (void) strcpy(outdir, Win32TempDir());
84 (void) strcat(outdir, "\\");
85 }
86 else
87#endif
88 if (strlen("/tmp/") < size) {
89 (void) strcpy(outdir, "/tmp/");
90 }
91}
92
93static Bool
94XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
95 XkbComponentNamesPtr names,
96 unsigned want,
97 unsigned need, char *nameRtrn, int nameRtrnLen)
98{
99 FILE *out;
100 char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX];
101
102 const char *emptystring = "";
103 char *xkbbasedirflag = NULL;
104 const char *xkbbindir = emptystring;
105 const char *xkbbindirsep = emptystring;
106
107#ifdef WIN32
108 /* WIN32 has no popen. The input must be stored in a file which is
109 used as input for xkbcomp. xkbcomp does not read from stdin. */
110 char tmpname[PATH_MAX];
111 const char *xkmfile = tmpname;
112#else
113 const char *xkmfile = "-";
114#endif
115
116 snprintf(keymap, sizeof(keymap), "server-%s", display);
117
118 OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
119
120#ifdef WIN32
121 strcpy(tmpname, Win32TempDir());
122 strcat(tmpname, "\\xkb_XXXXXX");
123 (void) mktemp(tmpname);
124#endif
125
126 if (XkbBaseDirectory != NULL) {
127 if (asprintf(&xkbbasedirflag, "\"-R%s\"", XkbBaseDirectory) == -1)
128 xkbbasedirflag = NULL;
129 }
130
131 if (XkbBinDirectory != NULL) {
132 int ld = strlen(XkbBinDirectory);
133 int lps = strlen(PATHSEPARATOR);
134
135 xkbbindir = XkbBinDirectory;
136
137 if ((ld >= lps) && (strcmp(xkbbindir + ld - lps, PATHSEPARATOR) != 0)) {
138 xkbbindirsep = PATHSEPARATOR;
139 }
140 }
141
142 if (asprintf(&buf,
143 "\"%s%sxkbcomp\" -w %d %s -xkm \"%s\" "
144 "-em1 %s -emp %s -eml %s \"%s%s.xkm\"",
145 xkbbindir, xkbbindirsep,
146 ((xkbDebugFlags < 2) ? 1 :
147 ((xkbDebugFlags > 10) ? 10 : (int) xkbDebugFlags)),
148 xkbbasedirflag ? xkbbasedirflag : "", xkmfile,
149 PRE_ERROR_MSG, ERROR_PREFIX, POST_ERROR_MSG1,
150 xkm_output_dir, keymap) == -1)
151 buf = NULL;
152
153 free(xkbbasedirflag);
154
155 if (!buf) {
156 LogMessage(X_ERROR,
157 "XKB: Could not invoke xkbcomp: not enough memory\n");
158 return FALSE;
159 }
160
161#ifndef WIN32
162 out = Popen(buf, "w");
163#else
164 out = fopen(tmpname, "w");
165#endif
166
167 if (out != NULL) {
168#ifdef DEBUG
169 if (xkbDebugFlags) {
170 ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
171 XkbWriteXKBKeymapForNames(stderr, names, xkb, want, need);
172 }
173#endif
174 XkbWriteXKBKeymapForNames(out, names, xkb, want, need);
175#ifndef WIN32
176 if (Pclose(out) == 0)
177#else
178 if (fclose(out) == 0 && System(buf) >= 0)
179#endif
180 {
181 if (xkbDebugFlags)
182 DebugF("[xkb] xkb executes: %s\n", buf);
183 if (nameRtrn) {
184 strlcpy(nameRtrn, keymap, nameRtrnLen);
185 }
186 free(buf);
187#ifdef WIN32
188 unlink(tmpname);
189#endif
190 return TRUE;
191 }
192 else
193 LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap);
194#ifdef WIN32
195 /* remove the temporary file */
196 unlink(tmpname);
197#endif
198 }
199 else {
200#ifndef WIN32
201 LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp\n");
202#else
203 LogMessage(X_ERROR, "Could not open file %s\n", tmpname);
204#endif
205 }
206 if (nameRtrn)
207 nameRtrn[0] = '\0';
208 free(buf);
209 return FALSE;
210}
211
212static FILE *
213XkbDDXOpenConfigFile(char *mapName, char *fileNameRtrn, int fileNameRtrnLen)
214{
215 char buf[PATH_MAX], xkm_output_dir[PATH_MAX];
216 FILE *file;
217
218 buf[0] = '\0';
219 if (mapName != NULL) {
220 OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
221 if ((XkbBaseDirectory != NULL) && (xkm_output_dir[0] != '/')
222#ifdef WIN32
223 && (!isalpha(xkm_output_dir[0]) || xkm_output_dir[1] != ':')
224#endif
225 ) {
226 if (snprintf(buf, PATH_MAX, "%s/%s%s.xkm", XkbBaseDirectory,
227 xkm_output_dir, mapName) >= PATH_MAX)
228 buf[0] = '\0';
229 }
230 else {
231 if (snprintf(buf, PATH_MAX, "%s%s.xkm", xkm_output_dir, mapName)
232 >= PATH_MAX)
233 buf[0] = '\0';
234 }
235 if (buf[0] != '\0')
236 file = fopen(buf, "rb");
237 else
238 file = NULL;
239 }
240 else
241 file = NULL;
242 if ((fileNameRtrn != NULL) && (fileNameRtrnLen > 0)) {
243 strlcpy(fileNameRtrn, buf, fileNameRtrnLen);
244 }
245 return file;
246}
247
248unsigned
249XkbDDXLoadKeymapByNames(DeviceIntPtr keybd,
250 XkbComponentNamesPtr names,
251 unsigned want,
252 unsigned need,
253 XkbDescPtr *xkbRtrn, char *nameRtrn, int nameRtrnLen)
254{
255 XkbDescPtr xkb;
256 FILE *file;
257 char fileName[PATH_MAX];
258 unsigned missing;
259
260 *xkbRtrn = NULL;
261 if ((keybd == NULL) || (keybd->key == NULL) ||
262 (keybd->key->xkbInfo == NULL))
263 xkb = NULL;
264 else
265 xkb = keybd->key->xkbInfo->desc;
266 if ((names->keycodes == NULL) && (names->types == NULL) &&
267 (names->compat == NULL) && (names->symbols == NULL) &&
268 (names->geometry == NULL)) {
269 LogMessage(X_ERROR, "XKB: No components provided for device %s\n",
270 keybd->name ? keybd->name : "(unnamed keyboard)");
271 return 0;
272 }
273 else if (!XkbDDXCompileKeymapByNames(xkb, names, want, need,
274 nameRtrn, nameRtrnLen)) {
275 LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
276 return 0;
277 }
278 file = XkbDDXOpenConfigFile(nameRtrn, fileName, PATH_MAX);
279 if (file == NULL) {
280 LogMessage(X_ERROR, "Couldn't open compiled keymap file %s\n",
281 fileName);
282 return 0;
283 }
284 missing = XkmReadFile(file, need, want, xkbRtrn);
285 if (*xkbRtrn == NULL) {
286 LogMessage(X_ERROR, "Error loading keymap %s\n", fileName);
287 fclose(file);
288 (void) unlink(fileName);
289 return 0;
290 }
291 else {
292 DebugF("Loaded XKB keymap %s, defined=0x%x\n", fileName,
293 (*xkbRtrn)->defined);
294 }
295 fclose(file);
296 (void) unlink(fileName);
297 return (need | want) & (~missing);
298}
299
300Bool
301XkbDDXNamesFromRules(DeviceIntPtr keybd,
302 char *rules_name,
303 XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names)
304{
305 char buf[PATH_MAX];
306 FILE *file;
307 Bool complete;
308 XkbRF_RulesPtr rules;
309
310 if (!rules_name)
311 return FALSE;
312
313 if (snprintf(buf, PATH_MAX, "%s/rules/%s", XkbBaseDirectory, rules_name)
314 >= PATH_MAX) {
315 LogMessage(X_ERROR, "XKB: Rules name is too long\n");
316 return FALSE;
317 }
318
319 file = fopen(buf, "r");
320 if (!file) {
321 LogMessage(X_ERROR, "XKB: Couldn't open rules file %s\n", buf);
322 return FALSE;
323 }
324
325 rules = XkbRF_Create();
326 if (!rules) {
327 LogMessage(X_ERROR, "XKB: Couldn't create rules struct\n");
328 fclose(file);
329 return FALSE;
330 }
331
332 if (!XkbRF_LoadRules(file, rules)) {
333 LogMessage(X_ERROR, "XKB: Couldn't parse rules file %s\n", rules_name);
334 fclose(file);
335 XkbRF_Free(rules, TRUE);
336 return FALSE;
337 }
338
339 memset(names, 0, sizeof(*names));
340 complete = XkbRF_GetComponents(rules, defs, names);
341 fclose(file);
342 XkbRF_Free(rules, TRUE);
343
344 if (!complete)
345 LogMessage(X_ERROR, "XKB: Rules returned no components\n");
346
347 return complete;
348}
349
350static Bool
351XkbRMLVOtoKcCGST(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
352 XkbComponentNamesPtr kccgst)
353{
354 XkbRF_VarDefsRec mlvo;
355
356 mlvo.model = rmlvo->model;
357 mlvo.layout = rmlvo->layout;
358 mlvo.variant = rmlvo->variant;
359 mlvo.options = rmlvo->options;
360
361 return XkbDDXNamesFromRules(dev, rmlvo->rules, &mlvo, kccgst);
362}
363
364/**
365 * Compile the given RMLVO keymap and return it. Returns the XkbDescPtr on
366 * success or NULL on failure. If the components compiled are not a superset
367 * or equal to need, the compiliation is treated as failure.
368 */
369static XkbDescPtr
370XkbCompileKeymapForDevice(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, int need)
371{
372 XkbDescPtr xkb = NULL;
373 unsigned int provided;
374 XkbComponentNamesRec kccgst = { 0 };
375 char name[PATH_MAX];
376
377 if (XkbRMLVOtoKcCGST(dev, rmlvo, &kccgst)) {
378 provided =
379 XkbDDXLoadKeymapByNames(dev, &kccgst, XkmAllIndicesMask, need, &xkb,
380 name, PATH_MAX);
381 if ((need & provided) != need) {
382 if (xkb) {
383 XkbFreeKeyboard(xkb, 0, TRUE);
384 xkb = NULL;
385 }
386 }
387 }
388
389 XkbFreeComponentNames(&kccgst, FALSE);
390 return xkb;
391}
392
393XkbDescPtr
394XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet * rmlvo)
395{
396 XkbDescPtr xkb;
397 unsigned int need;
398
399 if (!dev || !rmlvo) {
400 LogMessage(X_ERROR, "XKB: No device or RMLVO specified\n");
401 return NULL;
402 }
403
404 /* These are the components we really really need */
405 need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask |
406 XkmKeyNamesMask | XkmVirtualModsMask;
407
408 xkb = XkbCompileKeymapForDevice(dev, rmlvo, need);
409
410 if (!xkb) {
411 XkbRMLVOSet dflts;
412
413 /* we didn't get what we really needed. And that will likely leave
414 * us with a keyboard that doesn't work. Use the defaults instead */
415 LogMessage(X_ERROR, "XKB: Failed to load keymap. Loading default "
416 "keymap instead.\n");
417
418 XkbGetRulesDflts(&dflts);
419
420 xkb = XkbCompileKeymapForDevice(dev, &dflts, 0);
421
422 XkbFreeRMLVOSet(&dflts, FALSE);
423 }
424
425 return xkb;
426}