xkbcomp.c revision 07d2e718
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            result.xkb->device_spec = device_id;
1041        }
1042        else if (inputFormat == INPUT_XKM) /* parse xkm file */
1043        {
1044            unsigned tmp;
1045            bzero((char *) &result, sizeof(result));
1046            if ((result.xkb = XkbAllocKeyboard()) == NULL)
1047            {
1048                WSGO("Cannot allocate keyboard description\n");
1049                /* NOTREACHED */
1050            }
1051            tmp = XkmReadFile(file, 0, XkmKeymapLegal, &result);
1052            if (tmp == XkmKeymapLegal)
1053            {
1054                ERROR1("Cannot read XKM file \"%s\"\n", inputFile);
1055                ok = False;
1056            }
1057            result.xkb->device_spec = device_id;
1058        }
1059        else
1060        {
1061            INFO1("Errors encountered in %s; not compiled.\n", inputFile);
1062            ok = False;
1063        }
1064    }
1065    else if (inDpy != NULL)
1066    {
1067        bzero((char *) &result, sizeof(result));
1068        result.type = XkmKeymapFile;
1069        result.xkb = XkbGetMap(inDpy, XkbAllMapComponentsMask, device_id);
1070        if (result.xkb == NULL)
1071            WSGO("Cannot load keyboard description\n");
1072        if (XkbGetIndicatorMap(inDpy, ~0, result.xkb) != Success)
1073            WSGO("Could not load indicator map\n");
1074        if (XkbGetControls(inDpy, XkbAllControlsMask, result.xkb) != Success)
1075            WSGO("Could not load keyboard controls\n");
1076        if (XkbGetCompatMap(inDpy, XkbAllCompatMask, result.xkb) != Success)
1077            WSGO("Could not load compatibility map\n");
1078        if (XkbGetNames(inDpy, XkbAllNamesMask, result.xkb) != Success)
1079            WSGO("Could not load names\n");
1080        if ((status = XkbGetGeometry(inDpy, result.xkb)) != Success)
1081        {
1082            if (warningLevel > 3)
1083            {
1084                char buf[100];
1085                buf[0] = '\0';
1086                XGetErrorText(inDpy, status, buf, 100);
1087                WARN1("Could not load keyboard geometry for %s\n", inDpyName);
1088                ACTION1("%s\n", buf);
1089                ACTION("Resulting keymap file will not describe geometry\n");
1090            }
1091        }
1092        if (computeDflts)
1093            ok = (ComputeKbdDefaults(result.xkb) == Success);
1094        else
1095            ok = True;
1096    }
1097    else
1098    {
1099        fprintf(stderr, "Cannot open \"%s\" to compile\n", inputFile);
1100        ok = 0;
1101    }
1102    if (ok)
1103    {
1104        FILE *out = stdout;
1105        if ((inDpy != outDpy) &&
1106            (XkbChangeKbdDisplay(outDpy, &result) != Success))
1107        {
1108            WSGO2("Error converting keyboard display from %s to %s\n",
1109                  inDpyName, outDpyName);
1110            exit(1);
1111        }
1112        if (outputFile != NULL)
1113        {
1114            if (uStringEqual(outputFile, "-"))
1115                outputFile = "stdout";
1116            else
1117            {
1118                /*
1119                 * fix to prevent symlink attack (e.g.,
1120                 * ln -s /etc/passwd /var/tmp/server-0.xkm)
1121                 */
1122                /*
1123                 * this patch may have POSIX, Linux, or GNU libc bias
1124                 * -- Branden Robinson
1125                 */
1126                int outputFileFd;
1127                int binMode = 0;
1128                const char *openMode = "w";
1129                unlink(outputFile);
1130#ifdef O_BINARY
1131                switch (outputFormat)
1132                {
1133                case WANT_XKM_FILE:
1134                    binMode = O_BINARY;
1135                    openMode = "wb";
1136                    break;
1137                default:
1138                    binMode = 0;
1139                    break;
1140                }
1141#endif
1142                outputFileFd =
1143                    open(outputFile, O_WRONLY | O_CREAT | O_EXCL,
1144                         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
1145                         | S_IWOTH | binMode);
1146                if (outputFileFd < 0)
1147                {
1148                    ERROR1
1149                        ("Cannot open \"%s\" to write keyboard description\n",
1150                         outputFile);
1151                    ACTION("Exiting\n");
1152                    exit(1);
1153                }
1154#ifndef WIN32
1155                out = fdopen(outputFileFd, openMode);
1156#else
1157                close(outputFileFd);
1158                out = fopen(outputFile, "wb");
1159#endif
1160                /* end BR */
1161                if (out == NULL)
1162                {
1163                    ERROR1
1164                        ("Cannot open \"%s\" to write keyboard description\n",
1165                         outputFile);
1166                    ACTION("Exiting\n");
1167                    exit(1);
1168                }
1169            }
1170        }
1171        switch (outputFormat)
1172        {
1173        case WANT_XKM_FILE:
1174            ok = XkbWriteXKMFile(out, &result);
1175            break;
1176        case WANT_XKB_FILE:
1177            ok = XkbWriteXKBFile(out, &result, showImplicit, NULL, NULL);
1178            break;
1179        case WANT_C_HDR:
1180            ok = XkbWriteCFile(out, outputFile, &result);
1181            break;
1182        case WANT_X_SERVER:
1183            if (!(ok = XkbWriteToServer(&result)))
1184            {
1185                ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1186                       _XkbErrLocation ? _XkbErrLocation : "unknown");
1187                ACTION1("Couldn't write keyboard description to %s\n",
1188                        outDpyName);
1189            }
1190            break;
1191        default:
1192            WSGO1("Unknown output format %d\n", outputFormat);
1193            ACTION("No output file created\n");
1194            ok = False;
1195            break;
1196        }
1197        if (outputFormat != WANT_X_SERVER)
1198        {
1199            if (fclose(out))
1200            {
1201                ERROR1("Cannot close \"%s\" properly (not enough space?)\n",
1202                       outputFile);
1203                ok= False;
1204            }
1205            else if (!ok)
1206            {
1207                ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1208                       _XkbErrLocation ? _XkbErrLocation : "unknown");
1209            }
1210            if (!ok)
1211            {
1212                ACTION1("Output file \"%s\" removed\n", outputFile);
1213                unlink(outputFile);
1214            }
1215        }
1216    }
1217    if (inDpy)
1218        XCloseDisplay(inDpy);
1219    inDpy = NULL;
1220    if (outDpy)
1221        XCloseDisplay(outDpy);
1222    uFinishUp();
1223    return (ok == 0);
1224}
1225