xkbcomp.c revision 1d8c7986
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
76unsigned int debugFlags;
77
78static const char *fileTypeExt[] = {
79    "XXX",
80    "xkm",
81    "h",
82    "xkb",
83    "dir"
84};
85
86static unsigned inputFormat, outputFormat;
87char *rootDir;
88static char *inputFile;
89static char *inputMap;
90static char *outputFile;
91static char *inDpyName;
92static char *outDpyName;
93static Display *inDpy;
94static Display *outDpy;
95static Bool showImplicit = False;
96static Bool synch = False;
97static Bool computeDflts = False;
98static Bool xkblist = False;
99unsigned warningLevel = 5;
100unsigned verboseLevel = 0;
101unsigned dirsToStrip = 0;
102unsigned optionalParts = 0;
103static char *preErrorMsg = NULL;
104static char *postErrorMsg = NULL;
105static char *errorPrefix = NULL;
106static 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
113static void
114Usage(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
183static void
184setVerboseFlags(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
217static Bool
218parseArgs(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
830static Display *
831GetDisplay(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
873extern int yydebug;
874#endif
875
876int
877main(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}
1230