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