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