xkbcomp.c revision 34345a63
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    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
182static void
183setVerboseFlags(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
216static Bool
217parseArgs(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
825static Display *
826GetDisplay(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
867extern int yydebug;
868
869int
870main(int argc, char *argv[])
871{
872    FILE *file;         /* input file (or stdin) */
873    XkbFile *rtrn;
874    XkbFile *mapToUse;
875    int ok;
876    XkbFileInfo result;
877    Status status;
878
879    yyin = stdin;
880    uSetEntryFile(NullString);
881    uSetDebugFile(NullString);
882    uSetErrorFile(NullString);
883
884    XkbInitIncludePath();
885    if (!parseArgs(argc, argv))
886        exit(1);
887#ifdef DEBUG
888    if (debugFlags & 0x2)
889        yydebug = 1;
890#endif
891    if (preErrorMsg)
892        uSetPreErrorMessage(preErrorMsg);
893    if (errorPrefix)
894        uSetErrorPrefix(errorPrefix);
895    if (postErrorMsg)
896        uSetPostErrorMessage(postErrorMsg);
897    file = NULL;
898    XkbInitAtoms(NULL);
899    XkbAddDefaultDirectoriesToPath();
900    if (xkblist)
901    {
902        Bool gotSome;
903        gotSome = GenerateListing(outputFile);
904        if ((warningLevel > 7) && (!gotSome))
905            return -1;
906        return 0;
907    }
908    if (inputFile != NULL)
909    {
910        if (uStringEqual(inputFile, "-"))
911        {
912            file = stdin;
913            inputFile = "stdin";
914        }
915        else
916        {
917            file = fopen(inputFile, "r");
918        }
919    }
920    else if (inDpyName != NULL)
921    {
922        inDpy = GetDisplay(argv[0], inDpyName);
923        if (!inDpy)
924        {
925            ACTION("Exiting\n");
926            exit(1);
927        }
928    }
929    if (outDpyName != NULL)
930    {
931        outDpy = GetDisplay(argv[0], outDpyName);
932        if (!outDpy)
933        {
934            ACTION("Exiting\n");
935            exit(1);
936        }
937    }
938    if ((inDpy == NULL) && (outDpy == NULL))
939    {
940        int mjr, mnr;
941        mjr = XkbMajorVersion;
942        mnr = XkbMinorVersion;
943        if (!XkbLibraryVersion(&mjr, &mnr))
944        {
945            INFO3("%s was compiled with XKB version %d.%02d\n",
946                  argv[0], XkbMajorVersion, XkbMinorVersion);
947            ERROR2("X library supports incompatible version %d.%02d\n",
948                   mjr, mnr);
949            ACTION("Exiting\n");
950            exit(1);
951        }
952    }
953    if (file)
954    {
955        ok = True;
956        setScanState(inputFile, 1);
957        if ((inputFormat == INPUT_XKB) /* parse .xkb file */
958            && (XKBParseFile(file, &rtrn) && (rtrn != NULL)))
959        {
960            fclose(file);
961            mapToUse = rtrn;
962            if (inputMap != NULL) /* map specified on cmdline? */
963            {
964                while ((mapToUse)
965                       && (!uStringEqual(mapToUse->name, inputMap)))
966                {
967                    mapToUse = (XkbFile *) mapToUse->common.next;
968                }
969                if (!mapToUse)
970                {
971                    FATAL2("No map named \"%s\" in \"%s\"\n",
972                           inputMap, inputFile);
973                    /* NOTREACHED */
974                }
975            }
976            else if (rtrn->common.next != NULL)
977            {
978                /* look for map with XkbLC_Default flag. */
979                mapToUse = rtrn;
980                for (; mapToUse; mapToUse = (XkbFile *) mapToUse->common.next)
981                {
982                    if (mapToUse->flags & XkbLC_Default)
983                        break;
984                }
985                if (!mapToUse)
986                {
987                    mapToUse = rtrn;
988                    if (warningLevel > 4)
989                    {
990                        WARN1
991                            ("No map specified, but \"%s\" has several\n",
992                             inputFile);
993                        ACTION1
994                            ("Using the first defined map, \"%s\"\n",
995                             mapToUse->name);
996                    }
997                }
998            }
999            bzero((char *) &result, sizeof(result));
1000            result.type = mapToUse->type;
1001            if ((result.xkb = XkbAllocKeyboard()) == NULL)
1002            {
1003                WSGO("Cannot allocate keyboard description\n");
1004                /* NOTREACHED */
1005            }
1006            switch (mapToUse->type)
1007            {
1008            case XkmSemanticsFile:
1009            case XkmLayoutFile:
1010            case XkmKeymapFile:
1011                ok = CompileKeymap(mapToUse, &result, MergeReplace);
1012                break;
1013            case XkmKeyNamesIndex:
1014                ok = CompileKeycodes(mapToUse, &result, MergeReplace);
1015                break;
1016            case XkmTypesIndex:
1017                ok = CompileKeyTypes(mapToUse, &result, MergeReplace);
1018                break;
1019            case XkmSymbolsIndex:
1020                /* if it's just symbols, invent key names */
1021                result.xkb->flags |= AutoKeyNames;
1022                ok = False;
1023                break;
1024            case XkmCompatMapIndex:
1025                ok = CompileCompatMap(mapToUse, &result, MergeReplace, NULL);
1026                break;
1027            case XkmGeometryFile:
1028            case XkmGeometryIndex:
1029                /* if it's just a geometry, invent key names */
1030                result.xkb->flags |= AutoKeyNames;
1031                ok = CompileGeometry(mapToUse, &result, MergeReplace);
1032                break;
1033            default:
1034                WSGO1("Unknown file type %d\n", mapToUse->type);
1035                ok = False;
1036                break;
1037            }
1038        }
1039        else if (inputFormat == INPUT_XKM) /* parse xkm file */
1040        {
1041            unsigned tmp;
1042            bzero((char *) &result, sizeof(result));
1043            if ((result.xkb = XkbAllocKeyboard()) == NULL)
1044            {
1045                WSGO("Cannot allocate keyboard description\n");
1046                /* NOTREACHED */
1047            }
1048            tmp = XkmReadFile(file, 0, XkmKeymapLegal, &result);
1049            if (tmp == XkmKeymapLegal)
1050            {
1051                ERROR1("Cannot read XKM file \"%s\"\n", inputFile);
1052                ok = False;
1053            }
1054        }
1055        else
1056        {
1057            INFO1("Errors encountered in %s; not compiled.\n", inputFile);
1058            ok = False;
1059        }
1060    }
1061    else if (inDpy != NULL)
1062    {
1063        bzero((char *) &result, sizeof(result));
1064        result.type = XkmKeymapFile;
1065        result.xkb = XkbGetMap(inDpy, XkbAllMapComponentsMask, device_id);
1066        if (result.xkb == NULL)
1067            WSGO("Cannot load keyboard description\n");
1068        if (XkbGetIndicatorMap(inDpy, ~0, result.xkb) != Success)
1069            WSGO("Could not load indicator map\n");
1070        if (XkbGetControls(inDpy, XkbAllControlsMask, result.xkb) != Success)
1071            WSGO("Could not load keyboard controls\n");
1072        if (XkbGetCompatMap(inDpy, XkbAllCompatMask, result.xkb) != Success)
1073            WSGO("Could not load compatibility map\n");
1074        if (XkbGetNames(inDpy, XkbAllNamesMask, result.xkb) != Success)
1075            WSGO("Could not load names\n");
1076        if ((status = XkbGetGeometry(inDpy, result.xkb)) != Success)
1077        {
1078            if (warningLevel > 3)
1079            {
1080                char buf[100];
1081                buf[0] = '\0';
1082                XGetErrorText(inDpy, status, buf, 100);
1083                WARN1("Could not load keyboard geometry for %s\n", inDpyName);
1084                ACTION1("%s\n", buf);
1085                ACTION("Resulting keymap file will not describe geometry\n");
1086            }
1087        }
1088        if (computeDflts)
1089            ok = (ComputeKbdDefaults(result.xkb) == Success);
1090        else
1091            ok = True;
1092    }
1093    else
1094    {
1095        fprintf(stderr, "Cannot open \"%s\" to compile\n", inputFile);
1096        ok = 0;
1097    }
1098    if (ok)
1099    {
1100        FILE *out = stdout;
1101        if ((inDpy != outDpy) &&
1102            (XkbChangeKbdDisplay(outDpy, &result) != Success))
1103        {
1104            WSGO2("Error converting keyboard display from %s to %s\n",
1105                  inDpyName, outDpyName);
1106            exit(1);
1107        }
1108        if (outputFile != NULL)
1109        {
1110            if (uStringEqual(outputFile, "-"))
1111                outputFile = "stdout";
1112            else
1113            {
1114                /*
1115                 * fix to prevent symlink attack (e.g.,
1116                 * ln -s /etc/passwd /var/tmp/server-0.xkm)
1117                 */
1118                /*
1119                 * this patch may have POSIX, Linux, or GNU libc bias
1120                 * -- Branden Robinson
1121                 */
1122                int outputFileFd;
1123                int binMode = 0;
1124                const char *openMode = "w";
1125                unlink(outputFile);
1126#ifdef O_BINARY
1127                switch (outputFormat)
1128                {
1129                case WANT_XKM_FILE:
1130                    binMode = O_BINARY;
1131                    openMode = "wb";
1132                    break;
1133                default:
1134                    binMode = 0;
1135                    break;
1136                }
1137#endif
1138                outputFileFd =
1139                    open(outputFile, O_WRONLY | O_CREAT | O_EXCL,
1140                         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
1141                         | S_IWOTH | binMode);
1142                if (outputFileFd < 0)
1143                {
1144                    ERROR1
1145                        ("Cannot open \"%s\" to write keyboard description\n",
1146                         outputFile);
1147                    ACTION("Exiting\n");
1148                    exit(1);
1149                }
1150#ifndef WIN32
1151                out = fdopen(outputFileFd, openMode);
1152#else
1153                close(outputFileFd);
1154                out = fopen(outputFile, "wb");
1155#endif
1156                /* end BR */
1157                if (out == NULL)
1158                {
1159                    ERROR1
1160                        ("Cannot open \"%s\" to write keyboard description\n",
1161                         outputFile);
1162                    ACTION("Exiting\n");
1163                    exit(1);
1164                }
1165            }
1166        }
1167        switch (outputFormat)
1168        {
1169        case WANT_XKM_FILE:
1170            ok = XkbWriteXKMFile(out, &result);
1171            break;
1172        case WANT_XKB_FILE:
1173            ok = XkbWriteXKBFile(out, &result, showImplicit, NULL, NULL);
1174            break;
1175        case WANT_C_HDR:
1176            ok = XkbWriteCFile(out, outputFile, &result);
1177            break;
1178        case WANT_X_SERVER:
1179            if (!(ok = XkbWriteToServer(&result)))
1180            {
1181                ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1182                       _XkbErrLocation ? _XkbErrLocation : "unknown");
1183                ACTION1("Couldn't write keyboard description to %s\n",
1184                        outDpyName);
1185            }
1186            break;
1187        default:
1188            WSGO1("Unknown output format %d\n", outputFormat);
1189            ACTION("No output file created\n");
1190            ok = False;
1191            break;
1192        }
1193        if (outputFormat != WANT_X_SERVER)
1194        {
1195            fclose(out);
1196            if (!ok)
1197            {
1198                ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1199                       _XkbErrLocation ? _XkbErrLocation : "unknown");
1200                ACTION1("Output file \"%s\" removed\n", outputFile);
1201                unlink(outputFile);
1202            }
1203        }
1204    }
1205    if (inDpy)
1206        XCloseDisplay(inDpy);
1207    inDpy = NULL;
1208    if (outDpy)
1209        XCloseDisplay(outDpy);
1210    uFinishUp();
1211    return (ok == 0);
1212}
1213