xkbcomp.c revision c82dfdfb
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                WARN1("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] != '-') || (uStringEqual(argv[i], "-")))
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                    ACTION3
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                    ACTION1("\"%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            INFO1("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 messsages specified\n");
309                    ACTION2("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                    ACTION2("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                    ACTION2("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                    ACTION1("\"%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                    ACTION1("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                    ACTION2("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                    ACTION2("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                    ACTION1("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                            WARN1
497                                ("Illegal component for %s option\n",
498                                 argv[i - 1]);
499                            ACTION1
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                INFO1("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                    ACTION2("Using %s, ignoring %s\n", rootDir, argv[i]);
543                }
544            }
545            else
546            {
547                rootDir = &argv[i][2];
548                if (warningLevel > 8)
549                {
550                    WARN1("Changing root directory to \"%s\"\n", rootDir);
551                }
552                if ((chdir(rootDir) < 0) && (warningLevel > 0))
553                {
554                    WARN1("Couldn't change directory to \"%s\"\n", rootDir);
555                    ACTION("Root directory (-R) option ignored\n");
556                    rootDir = NULL;
557                }
558            }
559        }
560        else if ((strcmp(argv[i], "-synch") == 0)
561                 || (strcmp(argv[i], "-s") == 0))
562        {
563            synch = True;
564        }
565        else if (strncmp(argv[i], "-v", 2) == 0)
566        {
567            char *str;
568            if (argv[i][2] != '\0')
569                str = &argv[i][2];
570            else if ((i < (argc - 1)) && (argv[i + 1][0] != '-'))
571                str = argv[++i];
572            else
573                str = NULL;
574            if (str)
575                setVerboseFlags(str);
576        }
577        else if (strncmp(argv[i], "-w", 2) == 0)
578        {
579            unsigned long utmp;
580            char *tmp2;
581            /* If text is just after "-w" in the same word, then it must
582             * be a number and it is the warning level. Otherwise, if the
583             * next argument is a number, then it is the warning level,
584             * else the warning level is assumed to be 0.
585             */
586            if (argv[i][2] == '\0')
587            {
588                warningLevel = 0;
589                if (i < argc - 1)
590                {
591                    utmp = strtoul(argv[i+1], &tmp2, 10);
592                    if (argv[i+1][0] != '\0' && *tmp2 == '\0')
593                    {
594                        warningLevel = utmp > 10 ? 10 : utmp;
595                        i++;
596                    }
597                }
598            }
599            else
600            {
601                utmp = strtoul(&argv[i][2], &tmp2, 10);
602                if (*tmp2 == '\0')
603                    warningLevel = utmp > 10 ? 10 : utmp;
604                else
605                {
606                    ERROR1("Unknown flag \"%s\" on command line\n", argv[i]);
607                    Usage(argc, argv);
608                    return False;
609                }
610            }
611        }
612        else if ((strcmp(argv[i], "-xkb") == 0) && (!xkblist))
613        {
614            if ((outputFormat != WANT_DEFAULT)
615                && (outputFormat != WANT_XKB_FILE))
616            {
617                if (warningLevel > 0)
618                {
619                    WARN("Multiple output file formats specified\n");
620                    ACTION1("\"%s\" flag ignored\n", argv[i]);
621                }
622            }
623            else
624                outputFormat = WANT_XKB_FILE;
625        }
626        else if ((strcmp(argv[i], "-xkm") == 0) && (!xkblist))
627        {
628            if ((outputFormat != WANT_DEFAULT)
629                && (outputFormat != WANT_XKM_FILE))
630            {
631                if (warningLevel > 0)
632                {
633                    WARN("Multiple output file formats specified\n");
634                    ACTION1("\"%s\" flag ignored\n", argv[i]);
635                }
636            }
637            else
638                outputFormat = WANT_XKM_FILE;
639        }
640        else
641        {
642            ERROR1("Unknown flag \"%s\" on command line\n", argv[i]);
643            Usage(argc, argv);
644            return False;
645        }
646    }
647    if (xkblist)
648        inputFormat = INPUT_XKB;
649    else if (inputFile == NULL)
650    {
651        ERROR("No input file specified\n");
652        return False;
653    }
654    else if (uStringEqual(inputFile, "-"))
655    {
656        inputFormat = INPUT_XKB;
657    }
658#ifndef WIN32
659    else if (strchr(inputFile, ':') == NULL)
660    {
661#else
662    else if ((strchr(inputFile, ':') == NULL) || (strlen(inputFile) > 2 &&
663                                               isalpha(inputFile[0]) &&
664                                               inputFile[1] == ':'
665                                               && strchr(inputFile + 2,
666                                                         ':') == NULL))
667    {
668#endif
669        int len;
670        len = strlen(inputFile);
671        if (inputFile[len - 1] == ')')
672        {
673            char *tmpstr;
674            if ((tmpstr = strchr(inputFile, '(')) != NULL)
675            {
676                *tmpstr = '\0';
677                inputFile[len - 1] = '\0';
678                tmpstr++;
679                if (*tmpstr == '\0')
680                {
681                    WARN("Empty map in filename\n");
682                    ACTION("Ignored\n");
683                }
684                else if (inputMap == NULL)
685                {
686                    inputMap = uStringDup(tmpstr);
687                }
688                else
689                {
690                    WARN("Map specified in filename and with -m flag\n");
691                    ACTION1("map from name (\"%s\") ignored\n", tmpstr);
692                }
693            }
694            else
695            {
696                ERROR1("Illegal name \"%s\" for input file\n", inputFile);
697                return False;
698            }
699        }
700        if ((len > 4) && (strcmp(&inputFile[len - 4], ".xkm") == 0))
701        {
702            inputFormat = INPUT_XKM;
703        }
704        else
705        {
706            FILE *file;
707            file = fopen(inputFile, "r");
708            if (file)
709            {
710                if (XkmProbe(file))
711                    inputFormat = INPUT_XKM;
712                else
713                    inputFormat = INPUT_XKB;
714                fclose(file);
715            }
716            else
717            {
718                fprintf(stderr, "Cannot open \"%s\" for reading\n",
719                        inputFile);
720                return False;
721            }
722        }
723    }
724    else
725    {
726        inDpyName = inputFile;
727        inputFile = NULL;
728        inputFormat = INPUT_XKM;
729    }
730
731    if (outputFormat == WANT_DEFAULT)
732    {
733        if (xkblist)
734            outputFormat = WANT_LISTING;
735        else if (inputFormat == INPUT_XKB)
736            outputFormat = WANT_XKM_FILE;
737        else
738            outputFormat = WANT_XKB_FILE;
739    }
740    if ((outputFormat == WANT_LISTING) && (inputFormat != INPUT_XKB))
741    {
742        if (inputFile)
743            ERROR("Cannot generate a listing from a .xkm file (yet)\n");
744        else
745            ERROR("Cannot generate a listing from an X connection (yet)\n");
746        return False;
747    }
748    if (xkblist)
749    {
750        if (outputFile == NULL)
751            outputFile = uStringDup("-");
752        else if (strchr(outputFile, ':') != NULL)
753        {
754            ERROR("Cannot write a listing to an X connection\n");
755            return False;
756        }
757    }
758    else if ((!outputFile) && (inputFile) && uStringEqual(inputFile, "-"))
759    {
760        int len = strlen("stdin") + strlen(fileTypeExt[outputFormat]) + 2;
761        outputFile = uTypedCalloc(len, char);
762        if (outputFile == NULL)
763        {
764            WSGO("Cannot allocate space for output file name\n");
765            ACTION("Exiting\n");
766            exit(1);
767        }
768        snprintf(outputFile, len, "stdin.%s", fileTypeExt[outputFormat]);
769    }
770    else if ((outputFile == NULL) && (inputFile != NULL))
771    {
772        int len;
773        char *base, *ext;
774
775        if (inputMap == NULL)
776        {
777            base = strrchr(inputFile, '/');
778            if (base == NULL)
779                base = inputFile;
780            else
781                base++;
782        }
783        else
784            base = inputMap;
785
786        len = strlen(base) + strlen(fileTypeExt[outputFormat]) + 2;
787        outputFile = uTypedCalloc(len, char);
788        if (outputFile == NULL)
789        {
790            WSGO("Cannot allocate space for output file name\n");
791            ACTION("Exiting\n");
792            exit(1);
793        }
794        ext = strrchr(base, '.');
795        if (ext == NULL)
796            snprintf(outputFile, len, "%s.%s", base, fileTypeExt[outputFormat]);
797        else
798        {
799            strcpy(outputFile, base);
800            strcpy(&outputFile[ext - base + 1], fileTypeExt[outputFormat]);
801        }
802    }
803    else if (outputFile == NULL)
804    {
805        int len;
806        char *ch, *name, buf[128];
807        if (inDpyName[0] == ':')
808            snprintf(name = buf, sizeof(buf), "server%s", inDpyName);
809        else
810            name = inDpyName;
811
812        len = strlen(name) + strlen(fileTypeExt[outputFormat]) + 2;
813        outputFile = uTypedCalloc(len, char);
814        if (outputFile == NULL)
815        {
816            WSGO("Cannot allocate space for output file name\n");
817            ACTION("Exiting\n");
818            exit(1);
819        }
820        strcpy(outputFile, name);
821        for (ch = outputFile; (*ch) != '\0'; ch++)
822        {
823            if (*ch == ':')
824                *ch = '-';
825            else if (*ch == '.')
826                *ch = '_';
827        }
828        *ch++ = '.';
829        strcpy(ch, fileTypeExt[outputFormat]);
830    }
831#ifdef WIN32
832    else if (strlen(outputFile) > 2 &&
833             isalpha(outputFile[0]) &&
834             outputFile[1] == ':' && strchr(outputFile + 2, ':') == NULL)
835    {
836    }
837#endif
838    else if (strchr(outputFile, ':') != NULL)
839    {
840        outDpyName = outputFile;
841        outputFile = NULL;
842        outputFormat = WANT_X_SERVER;
843    }
844    return True;
845}
846
847static Display *
848GetDisplay(char *program, char *dpyName)
849{
850    int mjr, mnr, error;
851    Display *dpy;
852
853    mjr = XkbMajorVersion;
854    mnr = XkbMinorVersion;
855    dpy = XkbOpenDisplay(dpyName, NULL, NULL, &mjr, &mnr, &error);
856    if (dpy == NULL)
857    {
858        switch (error)
859        {
860        case XkbOD_BadLibraryVersion:
861            INFO3("%s was compiled with XKB version %d.%02d\n",
862                  program, XkbMajorVersion, XkbMinorVersion);
863            ERROR2("X library supports incompatible version %d.%02d\n",
864                   mjr, mnr);
865            break;
866        case XkbOD_ConnectionRefused:
867            ERROR1("Cannot open display \"%s\"\n", dpyName);
868            break;
869        case XkbOD_NonXkbServer:
870            ERROR1("XKB extension not present on %s\n", dpyName);
871            break;
872        case XkbOD_BadServerVersion:
873            INFO3("%s was compiled with XKB version %d.%02d\n",
874                  program, XkbMajorVersion, XkbMinorVersion);
875            ERROR3("Server %s uses incompatible version %d.%02d\n",
876                   dpyName, mjr, mnr);
877            break;
878        default:
879            WSGO1("Unknown error %d from XkbOpenDisplay\n", error);
880        }
881    }
882    else if (synch)
883        XSynchronize(dpy, True);
884    return dpy;
885}
886
887/***====================================================================***/
888
889#ifdef DEBUG
890extern int yydebug;
891#endif
892
893int
894main(int argc, char *argv[])
895{
896    FILE *file;         /* input file (or stdin) */
897    XkbFile *rtrn;
898    XkbFile *mapToUse;
899    int ok;
900    XkbFileInfo result;
901    Status status;
902
903    scan_set_file(stdin);
904    uSetDebugFile(NullString);
905    uSetErrorFile(NullString);
906
907    XkbInitIncludePath();
908    if (!parseArgs(argc, argv))
909        exit(1);
910#ifdef DEBUG
911    if (debugFlags & 0x2)
912        yydebug = 1;
913#endif
914    if (preErrorMsg)
915        uSetPreErrorMessage(preErrorMsg);
916    if (errorPrefix)
917        uSetErrorPrefix(errorPrefix);
918    if (postErrorMsg)
919        uSetPostErrorMessage(postErrorMsg);
920    file = NULL;
921    XkbInitAtoms(NULL);
922    XkbAddDefaultDirectoriesToPath();
923    if (xkblist)
924    {
925        Bool gotSome;
926        gotSome = GenerateListing(outputFile);
927        if ((warningLevel > 7) && (!gotSome))
928            return -1;
929        return 0;
930    }
931    if (inputFile != NULL)
932    {
933        if (uStringEqual(inputFile, "-"))
934        {
935            file = stdin;
936            inputFile = "stdin";
937        }
938        else
939        {
940            file = fopen(inputFile, "r");
941        }
942    }
943    else if (inDpyName != NULL)
944    {
945        inDpy = GetDisplay(argv[0], inDpyName);
946        if (!inDpy)
947        {
948            ACTION("Exiting\n");
949            exit(1);
950        }
951    }
952    if (outDpyName != NULL)
953    {
954        outDpy = GetDisplay(argv[0], outDpyName);
955        if (!outDpy)
956        {
957            ACTION("Exiting\n");
958            exit(1);
959        }
960    }
961    if ((inDpy == NULL) && (outDpy == NULL))
962    {
963        int mjr, mnr;
964        mjr = XkbMajorVersion;
965        mnr = XkbMinorVersion;
966        if (!XkbLibraryVersion(&mjr, &mnr))
967        {
968            INFO3("%s was compiled with XKB version %d.%02d\n",
969                  argv[0], XkbMajorVersion, XkbMinorVersion);
970            ERROR2("X library supports incompatible version %d.%02d\n",
971                   mjr, mnr);
972            ACTION("Exiting\n");
973            exit(1);
974        }
975    }
976    if (file)
977    {
978        ok = True;
979        setScanState(inputFile, 1);
980        if ((inputFormat == INPUT_XKB) /* parse .xkb file */
981            && (XKBParseFile(file, &rtrn) && (rtrn != NULL)))
982        {
983            fclose(file);
984            mapToUse = rtrn;
985            if (inputMap != NULL) /* map specified on cmdline? */
986            {
987                while ((mapToUse)
988                       && (!uStringEqual(mapToUse->name, inputMap)))
989                {
990                    mapToUse = (XkbFile *) mapToUse->common.next;
991                }
992                if (!mapToUse)
993                {
994                    FATAL2("No map named \"%s\" in \"%s\"\n",
995                           inputMap, inputFile);
996                    /* NOTREACHED */
997                }
998            }
999            else if (rtrn->common.next != NULL)
1000            {
1001                /* look for map with XkbLC_Default flag. */
1002                mapToUse = rtrn;
1003                for (; mapToUse; mapToUse = (XkbFile *) mapToUse->common.next)
1004                {
1005                    if (mapToUse->flags & XkbLC_Default)
1006                        break;
1007                }
1008                if (!mapToUse)
1009                {
1010                    mapToUse = rtrn;
1011                    if (warningLevel > 4)
1012                    {
1013                        WARN1
1014                            ("No map specified, but \"%s\" has several\n",
1015                             inputFile);
1016                        ACTION1
1017                            ("Using the first defined map, \"%s\"\n",
1018                             mapToUse->name);
1019                    }
1020                }
1021            }
1022            bzero((char *) &result, sizeof(result));
1023            result.type = mapToUse->type;
1024            if ((result.xkb = XkbAllocKeyboard()) == NULL)
1025            {
1026                WSGO("Cannot allocate keyboard description\n");
1027                /* NOTREACHED */
1028            }
1029            switch (mapToUse->type)
1030            {
1031            case XkmSemanticsFile:
1032            case XkmLayoutFile:
1033            case XkmKeymapFile:
1034                ok = CompileKeymap(mapToUse, &result, MergeReplace);
1035                break;
1036            case XkmKeyNamesIndex:
1037                ok = CompileKeycodes(mapToUse, &result, MergeReplace);
1038                break;
1039            case XkmTypesIndex:
1040                ok = CompileKeyTypes(mapToUse, &result, MergeReplace);
1041                break;
1042            case XkmSymbolsIndex:
1043                /* if it's just symbols, invent key names */
1044                result.xkb->flags |= AutoKeyNames;
1045                ok = False;
1046                break;
1047            case XkmCompatMapIndex:
1048                ok = CompileCompatMap(mapToUse, &result, MergeReplace, NULL);
1049                break;
1050            case XkmGeometryFile:
1051            case XkmGeometryIndex:
1052                /* if it's just a geometry, invent key names */
1053                result.xkb->flags |= AutoKeyNames;
1054                ok = CompileGeometry(mapToUse, &result, MergeReplace);
1055                break;
1056            default:
1057                WSGO1("Unknown file type %d\n", mapToUse->type);
1058                ok = False;
1059                break;
1060            }
1061            result.xkb->device_spec = device_id;
1062        }
1063        else if (inputFormat == INPUT_XKM) /* parse xkm file */
1064        {
1065            unsigned tmp;
1066            bzero((char *) &result, sizeof(result));
1067            if ((result.xkb = XkbAllocKeyboard()) == NULL)
1068            {
1069                WSGO("Cannot allocate keyboard description\n");
1070                /* NOTREACHED */
1071            }
1072            tmp = XkmReadFile(file, 0, XkmKeymapLegal, &result);
1073            if (tmp == XkmKeymapLegal)
1074            {
1075                ERROR1("Cannot read XKM file \"%s\"\n", inputFile);
1076                ok = False;
1077            }
1078            result.xkb->device_spec = device_id;
1079        }
1080        else
1081        {
1082            INFO1("Errors encountered in %s; not compiled.\n", inputFile);
1083            ok = False;
1084        }
1085    }
1086    else if (inDpy != NULL)
1087    {
1088        bzero((char *) &result, sizeof(result));
1089        result.type = XkmKeymapFile;
1090        result.xkb = XkbGetMap(inDpy, XkbAllMapComponentsMask, device_id);
1091        if (result.xkb == NULL)
1092            WSGO("Cannot load keyboard description\n");
1093        if (XkbGetIndicatorMap(inDpy, ~0, result.xkb) != Success)
1094            WSGO("Could not load indicator map\n");
1095        if (XkbGetControls(inDpy, XkbAllControlsMask, result.xkb) != Success)
1096            WSGO("Could not load keyboard controls\n");
1097        if (XkbGetCompatMap(inDpy, XkbAllCompatMask, result.xkb) != Success)
1098            WSGO("Could not load compatibility map\n");
1099        if (XkbGetNames(inDpy, XkbAllNamesMask, result.xkb) != Success)
1100            WSGO("Could not load names\n");
1101        if ((status = XkbGetGeometry(inDpy, result.xkb)) != Success)
1102        {
1103            if (warningLevel > 3)
1104            {
1105                char buf[100];
1106                buf[0] = '\0';
1107                XGetErrorText(inDpy, status, buf, 100);
1108                WARN1("Could not load keyboard geometry for %s\n", inDpyName);
1109                ACTION1("%s\n", buf);
1110                ACTION("Resulting keymap file will not describe geometry\n");
1111            }
1112        }
1113        if (computeDflts)
1114            ok = (ComputeKbdDefaults(result.xkb) == Success);
1115        else
1116            ok = True;
1117    }
1118    else
1119    {
1120        fprintf(stderr, "Cannot open \"%s\" to compile\n", inputFile);
1121        ok = 0;
1122    }
1123    if (ok)
1124    {
1125        FILE *out = stdout;
1126        if ((inDpy != outDpy) &&
1127            (XkbChangeKbdDisplay(outDpy, &result) != Success))
1128        {
1129            WSGO2("Error converting keyboard display from %s to %s\n",
1130                  inDpyName, outDpyName);
1131            exit(1);
1132        }
1133        if (outputFile != NULL)
1134        {
1135            if (uStringEqual(outputFile, "-"))
1136                outputFile = "stdout";
1137            else
1138            {
1139                /*
1140                 * fix to prevent symlink attack (e.g.,
1141                 * ln -s /etc/passwd /var/tmp/server-0.xkm)
1142                 */
1143                /*
1144                 * this patch may have POSIX, Linux, or GNU libc bias
1145                 * -- Branden Robinson
1146                 */
1147                int outputFileFd;
1148                int binMode = 0;
1149                const char *openMode = "w";
1150                unlink(outputFile);
1151#ifdef O_BINARY
1152                switch (outputFormat)
1153                {
1154                case WANT_XKM_FILE:
1155                    binMode = O_BINARY;
1156                    openMode = "wb";
1157                    break;
1158                default:
1159                    binMode = 0;
1160                    break;
1161                }
1162#endif
1163                outputFileFd =
1164                    open(outputFile, O_WRONLY | O_CREAT | O_EXCL,
1165                         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
1166                         | S_IWOTH | binMode);
1167                if (outputFileFd < 0)
1168                {
1169                    ERROR1
1170                        ("Cannot open \"%s\" to write keyboard description\n",
1171                         outputFile);
1172                    ACTION("Exiting\n");
1173                    exit(1);
1174                }
1175#ifndef WIN32
1176                out = fdopen(outputFileFd, openMode);
1177#else
1178                close(outputFileFd);
1179                out = fopen(outputFile, "wb");
1180#endif
1181                /* end BR */
1182                if (out == NULL)
1183                {
1184                    ERROR1
1185                        ("Cannot open \"%s\" to write keyboard description\n",
1186                         outputFile);
1187                    ACTION("Exiting\n");
1188                    exit(1);
1189                }
1190            }
1191        }
1192        switch (outputFormat)
1193        {
1194        case WANT_XKM_FILE:
1195            ok = XkbWriteXKMFile(out, &result);
1196            break;
1197        case WANT_XKB_FILE:
1198            ok = XkbWriteXKBFile(out, &result, showImplicit, NULL, NULL);
1199            break;
1200        case WANT_C_HDR:
1201            ok = XkbWriteCFile(out, outputFile, &result);
1202            break;
1203        case WANT_X_SERVER:
1204            if (!(ok = XkbWriteToServer(&result)))
1205            {
1206                ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1207                       _XkbErrLocation ? _XkbErrLocation : "unknown");
1208                ACTION1("Couldn't write keyboard description to %s\n",
1209                        outDpyName);
1210            }
1211            break;
1212        default:
1213            WSGO1("Unknown output format %d\n", outputFormat);
1214            ACTION("No output file created\n");
1215            ok = False;
1216            break;
1217        }
1218        if (outputFormat != WANT_X_SERVER)
1219        {
1220            if (fclose(out))
1221            {
1222                ERROR1("Cannot close \"%s\" properly (not enough space?)\n",
1223                       outputFile);
1224                ok= False;
1225            }
1226            else if (!ok)
1227            {
1228                ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1229                       _XkbErrLocation ? _XkbErrLocation : "unknown");
1230            }
1231            if (!ok)
1232            {
1233                ACTION1("Output file \"%s\" removed\n", outputFile);
1234                unlink(outputFile);
1235            }
1236        }
1237    }
1238    if (inDpy)
1239        XCloseDisplay(inDpy);
1240    inDpy = NULL;
1241    if (outDpy)
1242        XCloseDisplay(outDpy);
1243    uFinishUp();
1244    return (ok == 0);
1245}
1246