1 /************************************************************
2 Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc.
3 
4 Permission to use, copy, modify, and distribute this
5 software and its documentation for any purpose and without
6 fee is hereby granted, provided that the above copyright
7 notice appear in all copies and that both that copyright
8 notice and this permission notice appear in supporting
9 documentation, and that the name of Silicon Graphics not be
10 used in advertising or publicity pertaining to distribution
11 of the software without specific prior written permission.
12 Silicon Graphics makes no representation about the suitability
13 of this software for any purpose. It is provided "as is"
14 without any express or implied warranty.
15 
16 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23 THE 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 #define	PRE_ERROR_MSG "\"The XKEYBOARD keymap compiler (xkbcomp) reports:\""
49 #define	ERROR_PREFIX	"\"> \""
50 #define	POST_ERROR_MSG1 "\"Errors from xkbcomp are not fatal to the X server\""
51 #define	POST_ERROR_MSG2 "\"End of messages from xkbcomp\""
52 
53 #if defined(WIN32)
54 #define PATHSEPARATOR "\\"
55 #else
56 #define PATHSEPARATOR "/"
57 #endif
58 
59 static unsigned
60 LoadXKM(unsigned want, unsigned need, const char *keymap, XkbDescPtr *xkbRtrn);
61 
62 static void
OutputDirectory(char * outdir,size_t size)63 OutputDirectory(char *outdir, size_t size)
64 {
65 #ifndef WIN32
66     /* Can we write an xkm and then open it too? */
67     if (access(XKM_OUTPUT_DIR, W_OK | X_OK) == 0 &&
68         (strlen(XKM_OUTPUT_DIR) < size)) {
69         (void) strcpy(outdir, XKM_OUTPUT_DIR);
70     }
71     else
72 #else
73     if (strlen(Win32TempDir()) + 1 < size) {
74         (void) strcpy(outdir, Win32TempDir());
75         (void) strcat(outdir, "\\");
76     }
77     else
78 #endif
79     if (strlen("/tmp/") < size) {
80         (void) strcpy(outdir, "/tmp/");
81     }
82 }
83 
84 /**
85  * Callback invoked by XkbRunXkbComp. Write to out to talk to xkbcomp.
86  */
87 typedef void (*xkbcomp_buffer_callback)(FILE *out, void *userdata);
88 
89 /**
90  * Start xkbcomp, let the callback write into xkbcomp's stdin. When done,
91  * return a strdup'd copy of the file name we've written to.
92  */
93 static char *
RunXkbComp(xkbcomp_buffer_callback callback,void * userdata)94 RunXkbComp(xkbcomp_buffer_callback callback, void *userdata)
95 {
96     FILE *out;
97     char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX];
98 
99     const char *emptystring = "";
100     char *xkbbasedirflag = NULL;
101     const char *xkbbindir = emptystring;
102     const char *xkbbindirsep = emptystring;
103 
104 #ifdef WIN32
105     /* WIN32 has no popen. The input must be stored in a file which is
106        used as input for xkbcomp. xkbcomp does not read from stdin. */
107     char tmpname[PATH_MAX];
108     const char *xkmfile = tmpname;
109 #else
110     const char *xkmfile = "-";
111 #endif
112 
113     snprintf(keymap, sizeof(keymap), "server-%s", display);
114 
115     OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
116 
117 #ifdef WIN32
118     strcpy(tmpname, Win32TempDir());
119     strcat(tmpname, "\\xkb_XXXXXX");
120     (void) mktemp(tmpname);
121 #endif
122 
123     if (XkbBaseDirectory != NULL) {
124         if (asprintf(&xkbbasedirflag, "\"-R%s\"", XkbBaseDirectory) == -1)
125             xkbbasedirflag = NULL;
126     }
127 
128     if (XkbBinDirectory != NULL) {
129         int ld = strlen(XkbBinDirectory);
130         int lps = strlen(PATHSEPARATOR);
131 
132         xkbbindir = XkbBinDirectory;
133 
134         if ((ld >= lps) && (strcmp(xkbbindir + ld - lps, PATHSEPARATOR) != 0)) {
135             xkbbindirsep = PATHSEPARATOR;
136         }
137     }
138 
139     if (asprintf(&buf,
140                  "\"%s%sxkbcomp\" -w %d %s -xkm \"%s\" "
141                  "-em1 %s -emp %s -eml %s \"%s%s.xkm\"",
142                  xkbbindir, xkbbindirsep,
143                  ((xkbDebugFlags < 2) ? 1 :
144                   ((xkbDebugFlags > 10) ? 10 : (int) xkbDebugFlags)),
145                  xkbbasedirflag ? xkbbasedirflag : "", xkmfile,
146                  PRE_ERROR_MSG, ERROR_PREFIX, POST_ERROR_MSG1,
147                  xkm_output_dir, keymap) == -1)
148         buf = NULL;
149 
150     free(xkbbasedirflag);
151 
152     if (!buf) {
153         LogMessage(X_ERROR,
154                    "XKB: Could not invoke xkbcomp: not enough memory\n");
155         return NULL;
156     }
157 
158 #ifndef WIN32
159     out = Popen(buf, "w");
160 #else
161     out = fopen(tmpname, "w");
162 #endif
163 
164     if (out != NULL) {
165         /* Now write to xkbcomp */
166         (*callback)(out, userdata);
167 
168 #ifndef WIN32
169         if (Pclose(out) == 0)
170 #else
171         if (fclose(out) == 0 && System(buf) >= 0)
172 #endif
173         {
174             if (xkbDebugFlags)
175                 DebugF("[xkb] xkb executes: %s\n", buf);
176             free(buf);
177 #ifdef WIN32
178             unlink(tmpname);
179 #endif
180             return xnfstrdup(keymap);
181         }
182         else {
183             LogMessage(X_ERROR, "Error compiling keymap (%s) executing '%s'\n",
184                        keymap, buf);
185         }
186 #ifdef WIN32
187         /* remove the temporary file */
188         unlink(tmpname);
189 #endif
190     }
191     else {
192 #ifndef WIN32
193         LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp\n");
194 #else
195         LogMessage(X_ERROR, "Could not open file %s\n", tmpname);
196 #endif
197     }
198     free(buf);
199     return NULL;
200 }
201 
202 typedef struct {
203     XkbDescPtr xkb;
204     XkbComponentNamesPtr names;
205     unsigned int want;
206     unsigned int need;
207 } XkbKeymapNamesCtx;
208 
209 static void
xkb_write_keymap_for_names_cb(FILE * out,void * userdata)210 xkb_write_keymap_for_names_cb(FILE *out, void *userdata)
211 {
212     XkbKeymapNamesCtx *ctx = userdata;
213 #ifdef DEBUG
214     if (xkbDebugFlags) {
215         ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
216         XkbWriteXKBKeymapForNames(stderr, ctx->names, ctx->xkb, ctx->want, ctx->need);
217     }
218 #endif
219     XkbWriteXKBKeymapForNames(out, ctx->names, ctx->xkb, ctx->want, ctx->need);
220 }
221 
222 static Bool
XkbDDXCompileKeymapByNames(XkbDescPtr xkb,XkbComponentNamesPtr names,unsigned want,unsigned need,char * nameRtrn,int nameRtrnLen)223 XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
224                            XkbComponentNamesPtr names,
225                            unsigned want,
226                            unsigned need, char *nameRtrn, int nameRtrnLen)
227 {
228     char *keymap;
229     Bool rc = FALSE;
230     XkbKeymapNamesCtx ctx = {
231         .xkb = xkb,
232         .names = names,
233         .want = want,
234         .need = need
235     };
236 
237     keymap = RunXkbComp(xkb_write_keymap_for_names_cb, &ctx);
238 
239     if (keymap) {
240         if(nameRtrn)
241             strlcpy(nameRtrn, keymap, nameRtrnLen);
242 
243         free(keymap);
244         rc = TRUE;
245     } else if (nameRtrn)
246         *nameRtrn = '\0';
247 
248     return rc;
249 }
250 
251 typedef struct {
252     const char *keymap;
253     size_t len;
254 } XkbKeymapString;
255 
256 static void
xkb_write_keymap_string_cb(FILE * out,void * userdata)257 xkb_write_keymap_string_cb(FILE *out, void *userdata)
258 {
259     XkbKeymapString *s = userdata;
260     fwrite(s->keymap, s->len, 1, out);
261 }
262 
263 static unsigned int
XkbDDXLoadKeymapFromString(DeviceIntPtr keybd,const char * keymap,int keymap_length,unsigned int want,unsigned int need,XkbDescPtr * xkbRtrn)264 XkbDDXLoadKeymapFromString(DeviceIntPtr keybd,
265                           const char *keymap, int keymap_length,
266                           unsigned int want,
267                           unsigned int need,
268                           XkbDescPtr *xkbRtrn)
269 {
270     unsigned int have;
271     char *map_name;
272     XkbKeymapString map = {
273         .keymap = keymap,
274         .len = keymap_length
275     };
276 
277     *xkbRtrn = NULL;
278 
279     map_name = RunXkbComp(xkb_write_keymap_string_cb, &map);
280     if (!map_name) {
281         LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
282         return 0;
283     }
284 
285     have = LoadXKM(want, need, map_name, xkbRtrn);
286     free(map_name);
287 
288     return have;
289 }
290 
291 static FILE *
XkbDDXOpenConfigFile(const char * mapName,char * fileNameRtrn,int fileNameRtrnLen)292 XkbDDXOpenConfigFile(const char *mapName, char *fileNameRtrn, int fileNameRtrnLen)
293 {
294     char buf[PATH_MAX], xkm_output_dir[PATH_MAX];
295     FILE *file;
296 
297     buf[0] = '\0';
298     if (mapName != NULL) {
299         OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
300         if ((XkbBaseDirectory != NULL) && (xkm_output_dir[0] != '/')
301 #ifdef WIN32
302             && (!isalpha(xkm_output_dir[0]) || xkm_output_dir[1] != ':')
303 #endif
304             ) {
305             if (snprintf(buf, PATH_MAX, "%s/%s%s.xkm", XkbBaseDirectory,
306                          xkm_output_dir, mapName) >= PATH_MAX)
307                 buf[0] = '\0';
308         }
309         else {
310             if (snprintf(buf, PATH_MAX, "%s%s.xkm", xkm_output_dir, mapName)
311                 >= PATH_MAX)
312                 buf[0] = '\0';
313         }
314         if (buf[0] != '\0')
315             file = fopen(buf, "rb");
316         else
317             file = NULL;
318     }
319     else
320         file = NULL;
321     if ((fileNameRtrn != NULL) && (fileNameRtrnLen > 0)) {
322         strlcpy(fileNameRtrn, buf, fileNameRtrnLen);
323     }
324     return file;
325 }
326 
327 static unsigned
LoadXKM(unsigned want,unsigned need,const char * keymap,XkbDescPtr * xkbRtrn)328 LoadXKM(unsigned want, unsigned need, const char *keymap, XkbDescPtr *xkbRtrn)
329 {
330     FILE *file;
331     char fileName[PATH_MAX];
332     unsigned missing;
333 
334     file = XkbDDXOpenConfigFile(keymap, fileName, PATH_MAX);
335     if (file == NULL) {
336         LogMessage(X_ERROR, "Couldn't open compiled keymap file %s\n",
337                    fileName);
338         return 0;
339     }
340     missing = XkmReadFile(file, need, want, xkbRtrn);
341     if (*xkbRtrn == NULL) {
342         LogMessage(X_ERROR, "Error loading keymap %s\n", fileName);
343         fclose(file);
344         (void) unlink(fileName);
345         return 0;
346     }
347     else {
348         DebugF("Loaded XKB keymap %s, defined=0x%x\n", fileName,
349                (*xkbRtrn)->defined);
350     }
351     fclose(file);
352     (void) unlink(fileName);
353     return (need | want) & (~missing);
354 }
355 
356 unsigned
XkbDDXLoadKeymapByNames(DeviceIntPtr keybd,XkbComponentNamesPtr names,unsigned want,unsigned need,XkbDescPtr * xkbRtrn,char * nameRtrn,int nameRtrnLen)357 XkbDDXLoadKeymapByNames(DeviceIntPtr keybd,
358                         XkbComponentNamesPtr names,
359                         unsigned want,
360                         unsigned need,
361                         XkbDescPtr *xkbRtrn, char *nameRtrn, int nameRtrnLen)
362 {
363     XkbDescPtr xkb;
364 
365     *xkbRtrn = NULL;
366     if ((keybd == NULL) || (keybd->key == NULL) ||
367         (keybd->key->xkbInfo == NULL))
368         xkb = NULL;
369     else
370         xkb = keybd->key->xkbInfo->desc;
371     if ((names->keycodes == NULL) && (names->types == NULL) &&
372         (names->compat == NULL) && (names->symbols == NULL) &&
373         (names->geometry == NULL)) {
374         LogMessage(X_ERROR, "XKB: No components provided for device %s\n",
375                    keybd->name ? keybd->name : "(unnamed keyboard)");
376         return 0;
377     }
378     else if (!XkbDDXCompileKeymapByNames(xkb, names, want, need,
379                                          nameRtrn, nameRtrnLen)) {
380         LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
381         return 0;
382     }
383 
384     return LoadXKM(want, need, nameRtrn, xkbRtrn);
385 }
386 
387 Bool
XkbDDXNamesFromRules(DeviceIntPtr keybd,const char * rules_name,XkbRF_VarDefsPtr defs,XkbComponentNamesPtr names)388 XkbDDXNamesFromRules(DeviceIntPtr keybd,
389                      const char *rules_name,
390                      XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names)
391 {
392     char buf[PATH_MAX];
393     FILE *file;
394     Bool complete;
395     XkbRF_RulesPtr rules;
396 
397     if (!rules_name)
398         return FALSE;
399 
400     if (snprintf(buf, PATH_MAX, "%s/rules/%s", XkbBaseDirectory, rules_name)
401         >= PATH_MAX) {
402         LogMessage(X_ERROR, "XKB: Rules name is too long\n");
403         return FALSE;
404     }
405 
406     file = fopen(buf, "r");
407     if (!file) {
408         LogMessage(X_ERROR, "XKB: Couldn't open rules file %s\n", buf);
409         return FALSE;
410     }
411 
412     rules = XkbRF_Create();
413     if (!rules) {
414         LogMessage(X_ERROR, "XKB: Couldn't create rules struct\n");
415         fclose(file);
416         return FALSE;
417     }
418 
419     if (!XkbRF_LoadRules(file, rules)) {
420         LogMessage(X_ERROR, "XKB: Couldn't parse rules file %s\n", rules_name);
421         fclose(file);
422         XkbRF_Free(rules, TRUE);
423         return FALSE;
424     }
425 
426     memset(names, 0, sizeof(*names));
427     complete = XkbRF_GetComponents(rules, defs, names);
428     fclose(file);
429     XkbRF_Free(rules, TRUE);
430 
431     if (!complete)
432         LogMessage(X_ERROR, "XKB: Rules returned no components\n");
433 
434     return complete;
435 }
436 
437 static Bool
XkbRMLVOtoKcCGST(DeviceIntPtr dev,XkbRMLVOSet * rmlvo,XkbComponentNamesPtr kccgst)438 XkbRMLVOtoKcCGST(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
439                  XkbComponentNamesPtr kccgst)
440 {
441     XkbRF_VarDefsRec mlvo;
442 
443     mlvo.model = rmlvo->model;
444     mlvo.layout = rmlvo->layout;
445     mlvo.variant = rmlvo->variant;
446     mlvo.options = rmlvo->options;
447 
448     return XkbDDXNamesFromRules(dev, rmlvo->rules, &mlvo, kccgst);
449 }
450 
451 /**
452  * Compile the given RMLVO keymap and return it. Returns the XkbDescPtr on
453  * success or NULL on failure. If the components compiled are not a superset
454  * or equal to need, the compiliation is treated as failure.
455  */
456 static XkbDescPtr
XkbCompileKeymapForDevice(DeviceIntPtr dev,XkbRMLVOSet * rmlvo,int need)457 XkbCompileKeymapForDevice(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, int need)
458 {
459     XkbDescPtr xkb = NULL;
460     unsigned int provided;
461     XkbComponentNamesRec kccgst = { 0 };
462     char name[PATH_MAX];
463 
464     if (XkbRMLVOtoKcCGST(dev, rmlvo, &kccgst)) {
465         provided =
466             XkbDDXLoadKeymapByNames(dev, &kccgst, XkmAllIndicesMask, need, &xkb,
467                                     name, PATH_MAX);
468         if ((need & provided) != need) {
469             if (xkb) {
470                 XkbFreeKeyboard(xkb, 0, TRUE);
471                 xkb = NULL;
472             }
473         }
474     }
475 
476     XkbFreeComponentNames(&kccgst, FALSE);
477     return xkb;
478 }
479 
480 static XkbDescPtr
KeymapOrDefaults(DeviceIntPtr dev,XkbDescPtr xkb)481 KeymapOrDefaults(DeviceIntPtr dev, XkbDescPtr xkb)
482 {
483     XkbRMLVOSet dflts;
484 
485     if (xkb)
486         return xkb;
487 
488     /* we didn't get what we really needed. And that will likely leave
489      * us with a keyboard that doesn't work. Use the defaults instead */
490     LogMessage(X_ERROR, "XKB: Failed to load keymap. Loading default "
491                         "keymap instead.\n");
492 
493     XkbGetRulesDflts(&dflts);
494 
495     xkb = XkbCompileKeymapForDevice(dev, &dflts, 0);
496 
497     XkbFreeRMLVOSet(&dflts, FALSE);
498 
499     return xkb;
500 }
501 
502 
503 XkbDescPtr
XkbCompileKeymap(DeviceIntPtr dev,XkbRMLVOSet * rmlvo)504 XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet * rmlvo)
505 {
506     XkbDescPtr xkb;
507     unsigned int need;
508 
509     if (!dev || !rmlvo) {
510         LogMessage(X_ERROR, "XKB: No device or RMLVO specified\n");
511         return NULL;
512     }
513 
514     /* These are the components we really really need */
515     need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask |
516         XkmKeyNamesMask | XkmVirtualModsMask;
517 
518     xkb = XkbCompileKeymapForDevice(dev, rmlvo, need);
519 
520     return KeymapOrDefaults(dev, xkb);
521 }
522 
523 XkbDescPtr
XkbCompileKeymapFromString(DeviceIntPtr dev,const char * keymap,int keymap_length)524 XkbCompileKeymapFromString(DeviceIntPtr dev,
525                            const char *keymap, int keymap_length)
526 {
527     XkbDescPtr xkb;
528     unsigned int need, provided;
529 
530     if (!dev || !keymap) {
531         LogMessage(X_ERROR, "XKB: No device or keymap specified\n");
532         return NULL;
533     }
534 
535     /* These are the components we really really need */
536     need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask |
537            XkmKeyNamesMask | XkmVirtualModsMask;
538 
539     provided =
540         XkbDDXLoadKeymapFromString(dev, keymap, keymap_length,
541                                    XkmAllIndicesMask, need, &xkb);
542     if ((need & provided) != need) {
543         if (xkb) {
544             XkbFreeKeyboard(xkb, 0, TRUE);
545             xkb = NULL;
546         }
547     }
548 
549     return KeymapOrDefaults(dev, xkb);
550 }
551