b0df9207ee8b1357e11700743399c35571bab76c
[framework/uifw/xorg/util/x11-xkb-utils.git] / xkbcomp / xkbcomp.c
1 /************************************************************
2  Copyright (c) 1994 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 #include <stdio.h>
28 #include <ctype.h>
29 #include <X11/keysym.h>
30
31 /* for symlink attack security fix -- Branden Robinson */
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 /* end BR */
36
37 #if defined(sgi)
38 #include <malloc.h>
39 #endif
40
41 #define DEBUG_VAR debugFlags
42 #include "xkbcomp.h"
43 #include <stdlib.h>
44 #include "xkbpath.h"
45 #include "parseutils.h"
46 #include "misc.h"
47 #include "tokens.h"
48 #include <X11/extensions/XKBgeom.h>
49
50 #ifdef __UNIXOS2__
51 #define chdir _chdir2
52 #endif
53
54 #ifdef WIN32
55 #define S_IRGRP 0
56 #define S_IWGRP 0
57 #define S_IROTH 0
58 #define S_IWOTH 0
59 #endif
60
61 #define lowbit(x)       ((x) & (-(x)))
62
63 /***====================================================================***/
64
65 #define WANT_DEFAULT    0
66 #define WANT_XKM_FILE   1
67 #define WANT_C_HDR      2
68 #define WANT_XKB_FILE   3
69 #define WANT_X_SERVER   4
70 #define WANT_LISTING    5
71
72 #define INPUT_UNKNOWN   0
73 #define INPUT_XKB       1
74 #define INPUT_XKM       2
75
76 unsigned int debugFlags;
77
78 static const char *fileTypeExt[] = {
79     "XXX",
80     "xkm",
81     "h",
82     "xkb",
83     "dir"
84 };
85
86 static unsigned inputFormat, outputFormat;
87 char *rootDir;
88 static char *inputFile;
89 static char *inputMap;
90 static char *outputFile;
91 static char *inDpyName;
92 static char *outDpyName;
93 static Display *inDpy;
94 static Display *outDpy;
95 static Bool showImplicit = False;
96 static Bool synch = False;
97 static Bool computeDflts = False;
98 static Bool xkblist = False;
99 unsigned warningLevel = 5;
100 unsigned verboseLevel = 0;
101 unsigned dirsToStrip = 0;
102 unsigned optionalParts = 0;
103 static char *preErrorMsg = NULL;
104 static char *postErrorMsg = NULL;
105 static char *errorPrefix = NULL;
106 static unsigned int device_id = XkbUseCoreKbd;
107
108 /***====================================================================***/
109
110 #define M(m)    fprintf(stderr,(m))
111 #define M1(m,a) fprintf(stderr,(m),(a))
112
113 static void
114 Usage(int argc, char *argv[])
115 {
116     if (!xkblist)
117         M1("Usage: %s [options] input-file [ output-file ]\n", argv[0]);
118     else
119         M1("Usage: %s [options] file[(map)] ...\n", argv[0]);
120     M("Legal options:\n");
121     M("-?,-help             Print this message\n");
122     if (!xkblist)
123     {
124         M("-a                   Show all actions\n");
125         M("-C                   Create a C header file\n");
126     }
127 #ifdef DEBUG
128     M("-d [flags]           Report debugging information\n");
129 #endif
130     M("-em1 <msg>           Print <msg> before printing first error message\n");
131     M("-emp <msg>           Print <msg> at the start of each message line\n");
132     M("-eml <msg>           If there were any errors, print <msg> before exiting\n");
133     if (!xkblist)
134     {
135         M("-dflts               Compute defaults for missing parts\n");
136         M("-I[<dir>]            Specifies a top level directory for include\n");
137         M("                     directives. Multiple directories are legal.\n");
138         M("-l [flags]           List matching maps in the specified files\n");
139         M("                     f: list fully specified names\n");
140         M("                     h: also list hidden maps\n");
141         M("                     l: long listing (show flags)\n");
142         M("                     p: also list partial maps\n");
143         M("                     R: recursively list subdirectories\n");
144         M("                     default is all options off\n");
145     }
146     M("-i <deviceid>        Specifies device ID (not name) to compile for\n");
147     M("-m[ap] <map>         Specifies map to compile\n");
148     M("-o <file>            Specifies output file name\n");
149     if (!xkblist)
150     {
151         M("-opt[ional] <parts>  Specifies optional components of keymap\n");
152         M("                     Errors in optional parts are not fatal\n");
153         M("                     <parts> can be any combination of:\n");
154         M("                     c: compat map         g: geometry\n");
155         M("                     k: keycodes           s: symbols\n");
156         M("                     t: types\n");
157     }
158     if (xkblist)
159     {
160         M("-p <count>           Specifies the number of slashes to be stripped\n");
161         M("                     from the front of the map name on output\n");
162     }
163     M("-R[<DIR>]            Specifies the root directory for\n");
164     M("                     relative path names\n");
165     M("-synch               Force synchronization\n");
166     if (xkblist)
167     {
168         M("-v [<flags>]         Set level of detail for listing.\n");
169         M("                     flags are as for the -l option\n");
170     }
171     M("-w [<lvl>]           Set warning level (0=none, 10=all)\n");
172     if (!xkblist)
173     {
174         M("-xkb                 Create an XKB source (.xkb) file\n");
175         M("-xkm                 Create a compiled key map (.xkm) file\n");
176     }
177     return;
178 }
179
180 /***====================================================================***/
181
182 static void
183 setVerboseFlags(char *str)
184 {
185     for (; *str; str++)
186     {
187         switch (*str)
188         {
189         case 'f':
190             verboseLevel |= WantFullNames;
191             break;
192         case 'h':
193             verboseLevel |= WantHiddenMaps;
194             break;
195         case 'l':
196             verboseLevel |= WantLongListing;
197             break;
198         case 'p':
199             verboseLevel |= WantPartialMaps;
200             break;
201         case 'R':
202             verboseLevel |= ListRecursive;
203             break;
204         default:
205             if (warningLevel > 4)
206             {
207                 WARN1("Unknown verbose option \"%c\"\n", (unsigned int) *str);
208                 ACTION("Ignored\n");
209             }
210             break;
211         }
212     }
213     return;
214 }
215
216 static Bool
217 parseArgs(int argc, char *argv[])
218 {
219     register int i, tmp;
220
221     i = strlen(argv[0]);
222     tmp = strlen("xkblist");
223     if ((i >= tmp) && (strcmp(&argv[0][i - tmp], "xkblist") == 0))
224     {
225         xkblist = True;
226     }
227     for (i = 1; i < argc; i++)
228     {
229         int itmp;
230         if ((argv[i][0] != '-') || (uStringEqual(argv[i], "-")))
231         {
232             if (!xkblist)
233             {
234                 if (inputFile == NULL)
235                     inputFile = argv[i];
236                 else if (outputFile == NULL)
237                     outputFile = argv[i];
238                 else if (warningLevel > 0)
239                 {
240                     WARN("Too many file names on command line\n");
241                     ACTION3
242                         ("Compiling %s, writing to %s, ignoring %s\n",
243                          inputFile, outputFile, argv[i]);
244                 }
245             }
246             else if (!AddMatchingFiles(argv[i]))
247                 return False;
248         }
249         else if ((strcmp(argv[i], "-?") == 0)
250                  || (strcmp(argv[i], "-help") == 0))
251         {
252             Usage(argc, argv);
253             exit(0);
254         }
255         else if ((strcmp(argv[i], "-a") == 0) && (!xkblist))
256         {
257             showImplicit = True;
258         }
259         else if ((strcmp(argv[i], "-C") == 0) && (!xkblist))
260         {
261             if ((outputFormat != WANT_DEFAULT)
262                 && (outputFormat != WANT_C_HDR))
263             {
264                 if (warningLevel > 0)
265                 {
266                     WARN("Multiple output file formats specified\n");
267                     ACTION1("\"%s\" flag ignored\n", argv[i]);
268                 }
269             }
270             else
271                 outputFormat = WANT_C_HDR;
272         }
273 #ifdef DEBUG
274         else if (strcmp(argv[i], "-d") == 0)
275         {
276             if ((i >= (argc - 1)) || (!isdigit(argv[i + 1][0])))
277             {
278                 debugFlags = 1;
279             }
280             else
281             {
282                 if (sscanf(argv[++i], "%i", &itmp) == 1)
283                     debugFlags = itmp;
284             }
285             INFO1("Setting debug flags to %d\n", debugFlags);
286         }
287 #endif
288         else if ((strcmp(argv[i], "-dflts") == 0) && (!xkblist))
289         {
290             computeDflts = True;
291         }
292         else if (strcmp(argv[i], "-em1") == 0)
293         {
294             if (++i >= argc)
295             {
296                 if (warningLevel > 0)
297                 {
298                     WARN("No pre-error message specified\n");
299                     ACTION("Trailing \"-em1\" option ignored\n");
300                 }
301             }
302             else if (preErrorMsg != NULL)
303             {
304                 if (warningLevel > 0)
305                 {
306                     WARN("Multiple pre-error messsages specified\n");
307                     ACTION2("Compiling %s, ignoring %s\n",
308                             preErrorMsg, argv[i]);
309                 }
310             }
311             else
312                 preErrorMsg = argv[i];
313         }
314         else if (strcmp(argv[i], "-emp") == 0)
315         {
316             if (++i >= argc)
317             {
318                 if (warningLevel > 0)
319                 {
320                     WARN("No error prefix specified\n");
321                     ACTION("Trailing \"-emp\" option ignored\n");
322                 }
323             }
324             else if (errorPrefix != NULL)
325             {
326                 if (warningLevel > 0)
327                 {
328                     WARN("Multiple error prefixes specified\n");
329                     ACTION2("Compiling %s, ignoring %s\n",
330                             errorPrefix, argv[i]);
331                 }
332             }
333             else
334                 errorPrefix = argv[i];
335         }
336         else if (strcmp(argv[i], "-eml") == 0)
337         {
338             if (++i >= argc)
339             {
340                 if (warningLevel > 0)
341                 {
342                     WARN("No post-error message specified\n");
343                     ACTION("Trailing \"-eml\" option ignored\n");
344                 }
345             }
346             else if (postErrorMsg != NULL)
347             {
348                 if (warningLevel > 0)
349                 {
350                     WARN("Multiple post-error messages specified\n");
351                     ACTION2("Compiling %s, ignoring %s\n",
352                             postErrorMsg, argv[i]);
353                 }
354             }
355             else
356                 postErrorMsg = argv[i];
357         }
358         else if ((strncmp(argv[i], "-I", 2) == 0) && (!xkblist))
359         {
360             if (!XkbAddDirectoryToPath(&argv[i][2]))
361             {
362                 ACTION("Exiting\n");
363                 exit(1);
364             }
365         }
366         else if ((strncmp(argv[i], "-i", 2) == 0) && (!xkblist))
367         {
368             if (++i >= argc)
369             {
370                 if (warningLevel > 0)
371                     WARN("No device ID specified\n");
372             }
373             device_id = atoi(argv[i]);
374         }
375         else if ((strncmp(argv[i], "-l", 2) == 0) && (!xkblist))
376         {
377             if (outputFormat != WANT_DEFAULT)
378             {
379                 if (warningLevel > 0)
380                 {
381                     WARN("Multiple output file formats specified\n");
382                     ACTION1("\"%s\" flag ignored\n", argv[i]);
383                 }
384             }
385             else
386             {
387                 if (argv[i][2] != '\0')
388                     setVerboseFlags(&argv[i][2]);
389                 xkblist = True;
390                 if ((inputFile) && (!AddMatchingFiles(inputFile)))
391                     return False;
392                 else
393                     inputFile = NULL;
394                 if ((outputFile) && (!AddMatchingFiles(outputFile)))
395                     return False;
396                 else
397                     outputFile = NULL;
398             }
399         }
400         else if ((strcmp(argv[i], "-m") == 0)
401                  || (strcmp(argv[i], "-map") == 0))
402         {
403             if (++i >= argc)
404             {
405                 if (warningLevel > 0)
406                 {
407                     WARN("No map name specified\n");
408                     ACTION1("Trailing \"%s\" option ignored\n", argv[i - 1]);
409                 }
410             }
411             else if (xkblist)
412             {
413                 if (!AddMapOnly(argv[i]))
414                     return False;
415             }
416             else if (inputMap != NULL)
417             {
418                 if (warningLevel > 0)
419                 {
420                     WARN("Multiple map names specified\n");
421                     ACTION2("Compiling %s, ignoring %s\n", inputMap, argv[i]);
422                 }
423             }
424             else
425                 inputMap = argv[i];
426         }
427         else if ((strcmp(argv[i], "-merge") == 0) && (!xkblist))
428         {
429             /* Ignored */
430         }
431         else if (strcmp(argv[i], "-o") == 0)
432         {
433             if (++i >= argc)
434             {
435                 if (warningLevel > 0)
436                 {
437                     WARN("No output file specified\n");
438                     ACTION("Trailing \"-o\" option ignored\n");
439                 }
440             }
441             else if (outputFile != NULL)
442             {
443                 if (warningLevel > 0)
444                 {
445                     WARN("Multiple output files specified\n");
446                     ACTION2("Compiling %s, ignoring %s\n", outputFile,
447                             argv[i]);
448                 }
449             }
450             else
451                 outputFile = argv[i];
452         }
453         else if (((strcmp(argv[i], "-opt") == 0)
454                   || (strcmp(argv[i], "optional") == 0)) && (!xkblist))
455         {
456             if (++i >= argc)
457             {
458                 if (warningLevel > 0)
459                 {
460                     WARN("No optional components specified\n");
461                     ACTION1("Trailing \"%s\" option ignored\n", argv[i - 1]);
462                 }
463             }
464             else
465             {
466                 char *tmp2;
467                 for (tmp2 = argv[i]; (*tmp2 != '\0'); tmp2++)
468                 {
469                     switch (*tmp2)
470                     {
471                     case 'c':
472                     case 'C':
473                         optionalParts |= XkmCompatMapMask;
474                         break;
475                     case 'g':
476                     case 'G':
477                         optionalParts |= XkmGeometryMask;
478                         break;
479                     case 'k':
480                     case 'K':
481                         optionalParts |= XkmKeyNamesMask;
482                         break;
483                     case 's':
484                     case 'S':
485                         optionalParts |= XkmSymbolsMask;
486                         break;
487                     case 't':
488                     case 'T':
489                         optionalParts |= XkmTypesMask;
490                         break;
491                     default:
492                         if (warningLevel > 0)
493                         {
494                             WARN1
495                                 ("Illegal component for %s option\n",
496                                  argv[i - 1]);
497                             ACTION1
498                                 ("Ignoring unknown specifier \"%c\"\n",
499                                  (unsigned int) *tmp2);
500                         }
501                         break;
502                     }
503                 }
504             }
505         }
506         else if (strncmp(argv[i], "-p", 2) == 0)
507         {
508             if (isdigit(argv[i][2]))
509             {
510                 if (sscanf(&argv[i][2], "%i", &itmp) == 1)
511                     dirsToStrip = itmp;
512             }
513             else if ((i < (argc - 1)) && (isdigit(argv[i + 1][0])))
514             {
515                 if (sscanf(argv[++i], "%i", &itmp) == 1)
516                     dirsToStrip = itmp;
517             }
518             else
519             {
520                 dirsToStrip = 0;
521             }
522             if (warningLevel > 5)
523                 INFO1("Setting path count to %d\n", dirsToStrip);
524         }
525         else if (strncmp(argv[i], "-R", 2) == 0)
526         {
527             if (argv[i][2] == '\0')
528             {
529                 if (warningLevel > 0)
530                 {
531                     WARN("No root directory specified\n");
532                     ACTION("Ignoring -R option\n");
533                 }
534             }
535             else if (rootDir != NULL)
536             {
537                 if (warningLevel > 0)
538                 {
539                     WARN("Multiple root directories specified\n");
540                     ACTION2("Using %s, ignoring %s\n", rootDir, argv[i]);
541                 }
542             }
543             else
544             {
545                 rootDir = &argv[i][2];
546                 if (warningLevel > 8)
547                 {
548                     WARN1("Changing root directory to \"%s\"\n", rootDir);
549                 }
550                 if ((chdir(rootDir) < 0) && (warningLevel > 0))
551                 {
552                     WARN1("Couldn't change directory to \"%s\"\n", rootDir);
553                     ACTION("Root directory (-R) option ignored\n");
554                     rootDir = NULL;
555                 }
556             }
557         }
558         else if ((strcmp(argv[i], "-synch") == 0)
559                  || (strcmp(argv[i], "-s") == 0))
560         {
561             synch = True;
562         }
563         else if (strncmp(argv[i], "-v", 2) == 0)
564         {
565             char *str;
566             if (argv[i][2] != '\0')
567                 str = &argv[i][2];
568             else if ((i < (argc - 1)) && (argv[i + 1][0] != '-'))
569                 str = argv[++i];
570             else
571                 str = NULL;
572             if (str)
573                 setVerboseFlags(str);
574         }
575         else if (strncmp(argv[i], "-w", 2) == 0)
576         {
577             if ((i >= (argc - 1)) || (!isdigit(argv[i + 1][0])))
578             {
579                 warningLevel = 0;
580                 if (isdigit(argv[i][1]))
581                     if (sscanf(&argv[i][1], "%i", &itmp) == 1)
582                         warningLevel = itmp;
583             }
584             else
585             {
586                 if (sscanf(argv[++i], "%i", &itmp) == 1)
587                     warningLevel = itmp;
588             }
589         }
590         else if ((strcmp(argv[i], "-xkb") == 0) && (!xkblist))
591         {
592             if ((outputFormat != WANT_DEFAULT)
593                 && (outputFormat != WANT_XKB_FILE))
594             {
595                 if (warningLevel > 0)
596                 {
597                     WARN("Multiple output file formats specified\n");
598                     ACTION1("\"%s\" flag ignored\n", argv[i]);
599                 }
600             }
601             else
602                 outputFormat = WANT_XKB_FILE;
603         }
604         else if ((strcmp(argv[i], "-xkm") == 0) && (!xkblist))
605         {
606             if ((outputFormat != WANT_DEFAULT)
607                 && (outputFormat != WANT_XKM_FILE))
608             {
609                 if (warningLevel > 0)
610                 {
611                     WARN("Multiple output file formats specified\n");
612                     ACTION1("\"%s\" flag ignored\n", argv[i]);
613                 }
614             }
615             else
616                 outputFormat = WANT_XKM_FILE;
617         }
618         else
619         {
620             ERROR1("Unknown flag \"%s\" on command line\n", argv[i]);
621             Usage(argc, argv);
622             return False;
623         }
624     }
625     if (xkblist)
626         inputFormat = INPUT_XKB;
627     else if (inputFile == NULL)
628     {
629         ERROR("No input file specified\n");
630         return False;
631     }
632     else if (uStringEqual(inputFile, "-"))
633     {
634         inputFormat = INPUT_XKB;
635     }
636 #ifndef WIN32
637     else if (strchr(inputFile, ':') == NULL)
638     {
639 #else
640     else if ((strchr(inputFile, ':') == NULL) || (strlen(inputFile) > 2 &&
641                                                isalpha(inputFile[0]) &&
642                                                inputFile[1] == ':'
643                                                && strchr(inputFile + 2,
644                                                          ':') == NULL))
645     {
646 #endif
647         int len;
648         len = strlen(inputFile);
649         if (inputFile[len - 1] == ')')
650         {
651             char *tmp;
652             if ((tmp = strchr(inputFile, '(')) != NULL)
653             {
654                 *tmp = '\0';
655                 inputFile[len - 1] = '\0';
656                 tmp++;
657                 if (*tmp == '\0')
658                 {
659                     WARN("Empty map in filename\n");
660                     ACTION("Ignored\n");
661                 }
662                 else if (inputMap == NULL)
663                 {
664                     inputMap = uStringDup(tmp);
665                 }
666                 else
667                 {
668                     WARN("Map specified in filename and with -m flag\n");
669                     ACTION1("map from name (\"%s\") ignored\n", tmp);
670                 }
671             }
672             else
673             {
674                 ERROR1("Illegal name \"%s\" for input file\n", inputFile);
675                 return False;
676             }
677         }
678         if ((len > 4) && (strcmp(&inputFile[len - 4], ".xkm") == 0))
679         {
680             inputFormat = INPUT_XKM;
681         }
682         else
683         {
684             FILE *file;
685             file = fopen(inputFile, "r");
686             if (file)
687             {
688                 if (XkmProbe(file))
689                     inputFormat = INPUT_XKM;
690                 else
691                     inputFormat = INPUT_XKB;
692                 fclose(file);
693             }
694             else
695             {
696                 fprintf(stderr, "Cannot open \"%s\" for reading\n",
697                         inputFile);
698                 return False;
699             }
700         }
701     }
702     else
703     {
704         inDpyName = inputFile;
705         inputFile = NULL;
706         inputFormat = INPUT_XKM;
707     }
708
709     if (outputFormat == WANT_DEFAULT)
710     {
711         if (xkblist)
712             outputFormat = WANT_LISTING;
713         else if (inputFormat == INPUT_XKB)
714             outputFormat = WANT_XKM_FILE;
715         else
716             outputFormat = WANT_XKB_FILE;
717     }
718     if ((outputFormat == WANT_LISTING) && (inputFormat != INPUT_XKB))
719     {
720         if (inputFile)
721             ERROR("Cannot generate a listing from a .xkm file (yet)\n");
722         else
723             ERROR("Cannot generate a listing from an X connection (yet)\n");
724         return False;
725     }
726     if (xkblist)
727     {
728         if (outputFile == NULL)
729             outputFile = uStringDup("-");
730         else if (strchr(outputFile, ':') != NULL)
731         {
732             ERROR("Cannot write a listing to an X connection\n");
733             return False;
734         }
735     }
736     else if ((!outputFile) && (inputFile) && uStringEqual(inputFile, "-"))
737     {
738         int len = strlen("stdin") + strlen(fileTypeExt[outputFormat]) + 2;
739         outputFile = uTypedCalloc(len, char);
740         if (outputFile == NULL)
741         {
742             WSGO("Cannot allocate space for output file name\n");
743             ACTION("Exiting\n");
744             exit(1);
745         }
746         sprintf(outputFile, "stdin.%s", fileTypeExt[outputFormat]);
747     }
748     else if ((outputFile == NULL) && (inputFile != NULL))
749     {
750         int len;
751         char *base, *ext;
752
753         if (inputMap == NULL)
754         {
755             base = strrchr(inputFile, '/');
756             if (base == NULL)
757                 base = inputFile;
758             else
759                 base++;
760         }
761         else
762             base = inputMap;
763
764         len = strlen(base) + strlen(fileTypeExt[outputFormat]) + 2;
765         outputFile = uTypedCalloc(len, char);
766         if (outputFile == NULL)
767         {
768             WSGO("Cannot allocate space for output file name\n");
769             ACTION("Exiting\n");
770             exit(1);
771         }
772         ext = strrchr(base, '.');
773         if (ext == NULL)
774             sprintf(outputFile, "%s.%s", base, fileTypeExt[outputFormat]);
775         else
776         {
777             strcpy(outputFile, base);
778             strcpy(&outputFile[ext - base + 1], fileTypeExt[outputFormat]);
779         }
780     }
781     else if (outputFile == NULL)
782     {
783         int len;
784         char *ch, *name, buf[128];
785         if (inDpyName[0] == ':')
786             snprintf(name = buf, sizeof(buf), "server%s", inDpyName);
787         else
788             name = inDpyName;
789
790         len = strlen(name) + strlen(fileTypeExt[outputFormat]) + 2;
791         outputFile = uTypedCalloc(len, char);
792         if (outputFile == NULL)
793         {
794             WSGO("Cannot allocate space for output file name\n");
795             ACTION("Exiting\n");
796             exit(1);
797         }
798         strcpy(outputFile, name);
799         for (ch = outputFile; (*ch) != '\0'; ch++)
800         {
801             if (*ch == ':')
802                 *ch = '-';
803             else if (*ch == '.')
804                 *ch = '_';
805         }
806         *ch++ = '.';
807         strcpy(ch, fileTypeExt[outputFormat]);
808     }
809 #ifdef WIN32
810     else if (strlen(outputFile) > 2 &&
811              isalpha(outputFile[0]) &&
812              outputFile[1] == ':' && strchr(outputFile + 2, ':') == NULL)
813     {
814     }
815 #endif
816     else if (strchr(outputFile, ':') != NULL)
817     {
818         outDpyName = outputFile;
819         outputFile = NULL;
820         outputFormat = WANT_X_SERVER;
821     }
822     return True;
823 }
824
825 static Display *
826 GetDisplay(char *program, char *dpyName)
827 {
828     int mjr, mnr, error;
829     Display *dpy;
830
831     mjr = XkbMajorVersion;
832     mnr = XkbMinorVersion;
833     dpy = XkbOpenDisplay(dpyName, NULL, NULL, &mjr, &mnr, &error);
834     if (dpy == NULL)
835     {
836         switch (error)
837         {
838         case XkbOD_BadLibraryVersion:
839             INFO3("%s was compiled with XKB version %d.%02d\n",
840                   program, XkbMajorVersion, XkbMinorVersion);
841             ERROR2("X library supports incompatible version %d.%02d\n",
842                    mjr, mnr);
843             break;
844         case XkbOD_ConnectionRefused:
845             ERROR1("Cannot open display \"%s\"\n", dpyName);
846             break;
847         case XkbOD_NonXkbServer:
848             ERROR1("XKB extension not present on %s\n", dpyName);
849             break;
850         case XkbOD_BadServerVersion:
851             INFO3("%s was compiled with XKB version %d.%02d\n",
852                   program, XkbMajorVersion, XkbMinorVersion);
853             ERROR3("Server %s uses incompatible version %d.%02d\n",
854                    dpyName, mjr, mnr);
855             break;
856         default:
857             WSGO1("Unknown error %d from XkbOpenDisplay\n", error);
858         }
859     }
860     else if (synch)
861         XSynchronize(dpy, True);
862     return dpy;
863 }
864
865 /***====================================================================***/
866
867 #ifdef DEBUG
868 extern int yydebug;
869 #endif
870
871 int
872 main(int argc, char *argv[])
873 {
874     FILE *file;         /* input file (or stdin) */
875     XkbFile *rtrn;
876     XkbFile *mapToUse;
877     int ok;
878     XkbFileInfo result;
879     Status status;
880
881     yyin = stdin;
882     uSetEntryFile(NullString);
883     uSetDebugFile(NullString);
884     uSetErrorFile(NullString);
885
886     XkbInitIncludePath();
887     if (!parseArgs(argc, argv))
888         exit(1);
889 #ifdef DEBUG
890     if (debugFlags & 0x2)
891         yydebug = 1;
892 #endif
893     if (preErrorMsg)
894         uSetPreErrorMessage(preErrorMsg);
895     if (errorPrefix)
896         uSetErrorPrefix(errorPrefix);
897     if (postErrorMsg)
898         uSetPostErrorMessage(postErrorMsg);
899     file = NULL;
900     XkbInitAtoms(NULL);
901     XkbAddDefaultDirectoriesToPath();
902     if (xkblist)
903     {
904         Bool gotSome;
905         gotSome = GenerateListing(outputFile);
906         if ((warningLevel > 7) && (!gotSome))
907             return -1;
908         return 0;
909     }
910     if (inputFile != NULL)
911     {
912         if (uStringEqual(inputFile, "-"))
913         {
914             file = stdin;
915             inputFile = "stdin";
916         }
917         else
918         {
919             file = fopen(inputFile, "r");
920         }
921     }
922     else if (inDpyName != NULL)
923     {
924         inDpy = GetDisplay(argv[0], inDpyName);
925         if (!inDpy)
926         {
927             ACTION("Exiting\n");
928             exit(1);
929         }
930     }
931     if (outDpyName != NULL)
932     {
933         outDpy = GetDisplay(argv[0], outDpyName);
934         if (!outDpy)
935         {
936             ACTION("Exiting\n");
937             exit(1);
938         }
939     }
940     if ((inDpy == NULL) && (outDpy == NULL))
941     {
942         int mjr, mnr;
943         mjr = XkbMajorVersion;
944         mnr = XkbMinorVersion;
945         if (!XkbLibraryVersion(&mjr, &mnr))
946         {
947             INFO3("%s was compiled with XKB version %d.%02d\n",
948                   argv[0], XkbMajorVersion, XkbMinorVersion);
949             ERROR2("X library supports incompatible version %d.%02d\n",
950                    mjr, mnr);
951             ACTION("Exiting\n");
952             exit(1);
953         }
954     }
955     if (file)
956     {
957         ok = True;
958         setScanState(inputFile, 1);
959         if ((inputFormat == INPUT_XKB) /* parse .xkb file */
960             && (XKBParseFile(file, &rtrn) && (rtrn != NULL)))
961         {
962             fclose(file);
963             mapToUse = rtrn;
964             if (inputMap != NULL) /* map specified on cmdline? */
965             {
966                 while ((mapToUse)
967                        && (!uStringEqual(mapToUse->name, inputMap)))
968                 {
969                     mapToUse = (XkbFile *) mapToUse->common.next;
970                 }
971                 if (!mapToUse)
972                 {
973                     FATAL2("No map named \"%s\" in \"%s\"\n",
974                            inputMap, inputFile);
975                     /* NOTREACHED */
976                 }
977             }
978             else if (rtrn->common.next != NULL)
979             {
980                 /* look for map with XkbLC_Default flag. */
981                 mapToUse = rtrn;
982                 for (; mapToUse; mapToUse = (XkbFile *) mapToUse->common.next)
983                 {
984                     if (mapToUse->flags & XkbLC_Default)
985                         break;
986                 }
987                 if (!mapToUse)
988                 {
989                     mapToUse = rtrn;
990                     if (warningLevel > 4)
991                     {
992                         WARN1
993                             ("No map specified, but \"%s\" has several\n",
994                              inputFile);
995                         ACTION1
996                             ("Using the first defined map, \"%s\"\n",
997                              mapToUse->name);
998                     }
999                 }
1000             }
1001             bzero((char *) &result, sizeof(result));
1002             result.type = mapToUse->type;
1003             if ((result.xkb = XkbAllocKeyboard()) == NULL)
1004             {
1005                 WSGO("Cannot allocate keyboard description\n");
1006                 /* NOTREACHED */
1007             }
1008             switch (mapToUse->type)
1009             {
1010             case XkmSemanticsFile:
1011             case XkmLayoutFile:
1012             case XkmKeymapFile:
1013                 ok = CompileKeymap(mapToUse, &result, MergeReplace);
1014                 break;
1015             case XkmKeyNamesIndex:
1016                 ok = CompileKeycodes(mapToUse, &result, MergeReplace);
1017                 break;
1018             case XkmTypesIndex:
1019                 ok = CompileKeyTypes(mapToUse, &result, MergeReplace);
1020                 break;
1021             case XkmSymbolsIndex:
1022                 /* if it's just symbols, invent key names */
1023                 result.xkb->flags |= AutoKeyNames;
1024                 ok = False;
1025                 break;
1026             case XkmCompatMapIndex:
1027                 ok = CompileCompatMap(mapToUse, &result, MergeReplace, NULL);
1028                 break;
1029             case XkmGeometryFile:
1030             case XkmGeometryIndex:
1031                 /* if it's just a geometry, invent key names */
1032                 result.xkb->flags |= AutoKeyNames;
1033                 ok = CompileGeometry(mapToUse, &result, MergeReplace);
1034                 break;
1035             default:
1036                 WSGO1("Unknown file type %d\n", mapToUse->type);
1037                 ok = False;
1038                 break;
1039             }
1040         }
1041         else if (inputFormat == INPUT_XKM) /* parse xkm file */
1042         {
1043             unsigned tmp;
1044             bzero((char *) &result, sizeof(result));
1045             if ((result.xkb = XkbAllocKeyboard()) == NULL)
1046             {
1047                 WSGO("Cannot allocate keyboard description\n");
1048                 /* NOTREACHED */
1049             }
1050             tmp = XkmReadFile(file, 0, XkmKeymapLegal, &result);
1051             if (tmp == XkmKeymapLegal)
1052             {
1053                 ERROR1("Cannot read XKM file \"%s\"\n", inputFile);
1054                 ok = False;
1055             }
1056         }
1057         else
1058         {
1059             INFO1("Errors encountered in %s; not compiled.\n", inputFile);
1060             ok = False;
1061         }
1062     }
1063     else if (inDpy != NULL)
1064     {
1065         bzero((char *) &result, sizeof(result));
1066         result.type = XkmKeymapFile;
1067         result.xkb = XkbGetMap(inDpy, XkbAllMapComponentsMask, device_id);
1068         if (result.xkb == NULL)
1069             WSGO("Cannot load keyboard description\n");
1070         if (XkbGetIndicatorMap(inDpy, ~0, result.xkb) != Success)
1071             WSGO("Could not load indicator map\n");
1072         if (XkbGetControls(inDpy, XkbAllControlsMask, result.xkb) != Success)
1073             WSGO("Could not load keyboard controls\n");
1074         if (XkbGetCompatMap(inDpy, XkbAllCompatMask, result.xkb) != Success)
1075             WSGO("Could not load compatibility map\n");
1076         if (XkbGetNames(inDpy, XkbAllNamesMask, result.xkb) != Success)
1077             WSGO("Could not load names\n");
1078         if ((status = XkbGetGeometry(inDpy, result.xkb)) != Success)
1079         {
1080             if (warningLevel > 3)
1081             {
1082                 char buf[100];
1083                 buf[0] = '\0';
1084                 XGetErrorText(inDpy, status, buf, 100);
1085                 WARN1("Could not load keyboard geometry for %s\n", inDpyName);
1086                 ACTION1("%s\n", buf);
1087                 ACTION("Resulting keymap file will not describe geometry\n");
1088             }
1089         }
1090         if (computeDflts)
1091             ok = (ComputeKbdDefaults(result.xkb) == Success);
1092         else
1093             ok = True;
1094     }
1095     else
1096     {
1097         fprintf(stderr, "Cannot open \"%s\" to compile\n", inputFile);
1098         ok = 0;
1099     }
1100     if (ok)
1101     {
1102         FILE *out = stdout;
1103         if ((inDpy != outDpy) &&
1104             (XkbChangeKbdDisplay(outDpy, &result) != Success))
1105         {
1106             WSGO2("Error converting keyboard display from %s to %s\n",
1107                   inDpyName, outDpyName);
1108             exit(1);
1109         }
1110         if (outputFile != NULL)
1111         {
1112             if (uStringEqual(outputFile, "-"))
1113                 outputFile = "stdout";
1114             else
1115             {
1116                 /*
1117                  * fix to prevent symlink attack (e.g.,
1118                  * ln -s /etc/passwd /var/tmp/server-0.xkm)
1119                  */
1120                 /*
1121                  * this patch may have POSIX, Linux, or GNU libc bias
1122                  * -- Branden Robinson
1123                  */
1124                 int outputFileFd;
1125                 int binMode = 0;
1126                 const char *openMode = "w";
1127                 unlink(outputFile);
1128 #ifdef O_BINARY
1129                 switch (outputFormat)
1130                 {
1131                 case WANT_XKM_FILE:
1132                     binMode = O_BINARY;
1133                     openMode = "wb";
1134                     break;
1135                 default:
1136                     binMode = 0;
1137                     break;
1138                 }
1139 #endif
1140                 outputFileFd =
1141                     open(outputFile, O_WRONLY | O_CREAT | O_EXCL,
1142                          S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
1143                          | S_IWOTH | binMode);
1144                 if (outputFileFd < 0)
1145                 {
1146                     ERROR1
1147                         ("Cannot open \"%s\" to write keyboard description\n",
1148                          outputFile);
1149                     ACTION("Exiting\n");
1150                     exit(1);
1151                 }
1152 #ifndef WIN32
1153                 out = fdopen(outputFileFd, openMode);
1154 #else
1155                 close(outputFileFd);
1156                 out = fopen(outputFile, "wb");
1157 #endif
1158                 /* end BR */
1159                 if (out == NULL)
1160                 {
1161                     ERROR1
1162                         ("Cannot open \"%s\" to write keyboard description\n",
1163                          outputFile);
1164                     ACTION("Exiting\n");
1165                     exit(1);
1166                 }
1167             }
1168         }
1169         switch (outputFormat)
1170         {
1171         case WANT_XKM_FILE:
1172             ok = XkbWriteXKMFile(out, &result);
1173             break;
1174         case WANT_XKB_FILE:
1175             ok = XkbWriteXKBFile(out, &result, showImplicit, NULL, NULL);
1176             break;
1177         case WANT_C_HDR:
1178             ok = XkbWriteCFile(out, outputFile, &result);
1179             break;
1180         case WANT_X_SERVER:
1181             if (!(ok = XkbWriteToServer(&result)))
1182             {
1183                 ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1184                        _XkbErrLocation ? _XkbErrLocation : "unknown");
1185                 ACTION1("Couldn't write keyboard description to %s\n",
1186                         outDpyName);
1187             }
1188             break;
1189         default:
1190             WSGO1("Unknown output format %d\n", outputFormat);
1191             ACTION("No output file created\n");
1192             ok = False;
1193             break;
1194         }
1195         if (outputFormat != WANT_X_SERVER)
1196         {
1197             if (fclose(out))
1198             {
1199                 ERROR1("Cannot close \"%s\" properly (not enough space?)\n",
1200                        outputFile);
1201                 ok= False;
1202             }
1203             else if (!ok)
1204             {
1205                 ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1206                        _XkbErrLocation ? _XkbErrLocation : "unknown");
1207             }
1208             if (!ok)
1209             {
1210                 ACTION1("Output file \"%s\" removed\n", outputFile);
1211                 unlink(outputFile);
1212             }
1213         }
1214     }
1215     if (inDpy)
1216         XCloseDisplay(inDpy);
1217     inDpy = NULL;
1218     if (outDpy)
1219         XCloseDisplay(outDpy);
1220     uFinishUp();
1221     return (ok == 0);
1222 }