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