xkbcomp.c revision b7d0e977
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#define	DEBUG_VAR debugFlags
38#include "xkbcomp.h"
39#include <stdlib.h>
40#include "xkbpath.h"
41#include "parseutils.h"
42#include "misc.h"
43#include "tokens.h"
44#include <X11/extensions/XKBgeom.h>
45
46
47#ifdef WIN32
48#define S_IRGRP 0
49#define S_IWGRP 0
50#define S_IROTH 0
51#define S_IWOTH 0
52#endif
53
54#define	lowbit(x)	((x) & (-(x)))
55
56/***====================================================================***/
57
58#define	WANT_DEFAULT	0
59#define	WANT_XKM_FILE	1
60#define	WANT_C_HDR	2
61#define	WANT_XKB_FILE	3
62#define	WANT_X_SERVER	4
63#define	WANT_LISTING	5
64
65#define	INPUT_UNKNOWN	0
66#define	INPUT_XKB	1
67#define	INPUT_XKM	2
68
69#ifdef DEBUG
70unsigned int debugFlags;
71#endif
72
73static const char *fileTypeExt[] = {
74    "XXX",
75    "xkm",
76    "h",
77    "xkb",
78    "dir"
79};
80
81static unsigned inputFormat, outputFormat;
82static const char *rootDir;
83static char *inputFile;
84static const char *inputMap;
85static char *outputFile;
86static const char *inDpyName;
87static const char *outDpyName;
88static Display *inDpy;
89static Display *outDpy;
90static Bool showImplicit = False;
91static Bool synch = False;
92static Bool computeDflts = False;
93static Bool xkblist = False;
94unsigned warningLevel = 5;
95unsigned verboseLevel = 0;
96unsigned dirsToStrip = 0;
97static unsigned optionalParts = 0;
98static const char *preErrorMsg = NULL;
99static const char *postErrorMsg = NULL;
100static const char *errorPrefix = NULL;
101static unsigned int device_id = XkbUseCoreKbd;
102
103/***====================================================================***/
104
105#define	M(m)	fprintf(stderr,(m))
106#define	M1(m,a)	fprintf(stderr,(m),(a))
107
108static void
109Usage(int argc, char *argv[])
110{
111    if (!xkblist)
112        M1("Usage: %s [options] input-file [ output-file ]\n", argv[0]);
113    else
114        M1("Usage: %s [options] file[(map)] ...\n", argv[0]);
115    M("Legal options:\n");
116    M("-?,-help             Print this message\n");
117    M("-version             Print the version number\n");
118    if (!xkblist)
119    {
120        M("-a                   Show all actions\n");
121        M("-C                   Create a C header file\n");
122    }
123#ifdef DEBUG
124    M("-d [flags]           Report debugging information\n");
125#endif
126    M("-em1 <msg>           Print <msg> before printing first error message\n");
127    M("-emp <msg>           Print <msg> at the start of each message line\n");
128    M("-eml <msg>           If there were any errors, print <msg> before exiting\n");
129    if (!xkblist)
130    {
131        M("-dflts               Compute defaults for missing parts\n");
132        M("-I[<dir>]            Specifies a top level directory for include\n");
133        M("                     directives. Multiple directories are legal.\n");
134        M("-l [flags]           List matching maps in the specified files\n");
135        M("                     f: list fully specified names\n");
136        M("                     h: also list hidden maps\n");
137        M("                     l: long listing (show flags)\n");
138        M("                     p: also list partial maps\n");
139        M("                     R: recursively list subdirectories\n");
140        M("                     default is all options off\n");
141    }
142    M("-i <deviceid>        Specifies device ID (not name) to compile for\n");
143    M("-m[ap] <map>         Specifies map to compile\n");
144    M("-o <file>            Specifies output file name\n");
145    if (!xkblist)
146    {
147        M("-opt[ional] <parts>  Specifies optional components of keymap\n");
148        M("                     Errors in optional parts are not fatal\n");
149        M("                     <parts> can be any combination of:\n");
150        M("                     c: compat map         g: geometry\n");
151        M("                     k: keycodes           s: symbols\n");
152        M("                     t: types\n");
153    }
154    if (xkblist)
155    {
156        M("-p <count>           Specifies the number of slashes to be stripped\n");
157        M("                     from the front of the map name on output\n");
158    }
159    M("-R[<DIR>]            Specifies the root directory for\n");
160    M("                     relative path names\n");
161    M("-synch               Force synchronization\n");
162    if (xkblist)
163    {
164        M("-v [<flags>]         Set level of detail for listing.\n");
165        M("                     flags are as for the -l option\n");
166    }
167    M("-w [<lvl>]           Set warning level (0=none, 10=all)\n");
168    if (!xkblist)
169    {
170        M("-xkb                 Create an XKB source (.xkb) file\n");
171        M("-xkm                 Create a compiled key map (.xkm) file\n");
172    }
173    return;
174}
175
176/***====================================================================***/
177
178static void
179setVerboseFlags(const char *str)
180{
181    for (; *str; str++)
182    {
183        switch (*str)
184        {
185        case 'f':
186            verboseLevel |= WantFullNames;
187            break;
188        case 'h':
189            verboseLevel |= WantHiddenMaps;
190            break;
191        case 'l':
192            verboseLevel |= WantLongListing;
193            break;
194        case 'p':
195            verboseLevel |= WantPartialMaps;
196            break;
197        case 'R':
198            verboseLevel |= ListRecursive;
199            break;
200        default:
201            if (warningLevel > 4)
202            {
203                WARN("Unknown verbose option \"%c\"\n", (unsigned int) *str);
204                ACTION("Ignored\n");
205            }
206            break;
207        }
208    }
209    return;
210}
211
212static Bool
213parseArgs(int argc, char *argv[])
214{
215    int i, tmp;
216
217    i = strlen(argv[0]);
218    tmp = strlen("xkblist");
219    if ((i >= tmp) && (strcmp(&argv[0][i - tmp], "xkblist") == 0))
220    {
221        xkblist = True;
222    }
223    for (i = 1; i < argc; i++)
224    {
225        int itmp;
226        if ((argv[i][0] != '-') || (strcmp(argv[i], "-") == 0))
227        {
228            if (!xkblist)
229            {
230                if (inputFile == NULL)
231                    inputFile = argv[i];
232                else if (outputFile == NULL)
233                    outputFile = argv[i];
234                else if (warningLevel > 0)
235                {
236                    WARN("Too many file names on command line\n");
237                    ACTION
238                        ("Compiling %s, writing to %s, ignoring %s\n",
239                         inputFile, outputFile, argv[i]);
240                }
241            }
242            else if (!AddMatchingFiles(argv[i]))
243                return False;
244        }
245        else if ((strcmp(argv[i], "-?") == 0)
246                 || (strcmp(argv[i], "-help") == 0))
247        {
248            Usage(argc, argv);
249            exit(0);
250        }
251        else if (strcmp(argv[i], "-version") == 0)
252        {
253            printf("xkbcomp %s\n", PACKAGE_VERSION);
254            exit(0);
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                    ACTION("\"%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            INFO("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 messages specified\n");
307                    ACTION("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                    ACTION("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                    ACTION("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                    ACTION("\"%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                    ACTION("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                    ACTION("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                    ACTION("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                    ACTION("Trailing \"%s\" option ignored\n", argv[i - 1]);
462                }
463            }
464            else
465            {
466                for (const char *tmp2 = argv[i]; (*tmp2 != '\0'); tmp2++)
467                {
468                    switch (*tmp2)
469                    {
470                    case 'c':
471                    case 'C':
472                        optionalParts |= XkmCompatMapMask;
473                        break;
474                    case 'g':
475                    case 'G':
476                        optionalParts |= XkmGeometryMask;
477                        break;
478                    case 'k':
479                    case 'K':
480                        optionalParts |= XkmKeyNamesMask;
481                        break;
482                    case 's':
483                    case 'S':
484                        optionalParts |= XkmSymbolsMask;
485                        break;
486                    case 't':
487                    case 'T':
488                        optionalParts |= XkmTypesMask;
489                        break;
490                    default:
491                        if (warningLevel > 0)
492                        {
493                            WARN
494                                ("Illegal component for %s option\n",
495                                 argv[i - 1]);
496                            ACTION
497                                ("Ignoring unknown specifier \"%c\"\n",
498                                 (unsigned int) *tmp2);
499                        }
500                        break;
501                    }
502                }
503            }
504        }
505        else if (strncmp(argv[i], "-p", 2) == 0)
506        {
507            if (isdigit(argv[i][2]))
508            {
509                if (sscanf(&argv[i][2], "%i", &itmp) == 1)
510                    dirsToStrip = itmp;
511            }
512            else if ((i < (argc - 1)) && (isdigit(argv[i + 1][0])))
513            {
514                if (sscanf(argv[++i], "%i", &itmp) == 1)
515                    dirsToStrip = itmp;
516            }
517            else
518            {
519                dirsToStrip = 0;
520            }
521            if (warningLevel > 5)
522                INFO("Setting path count to %d\n", dirsToStrip);
523        }
524        else if (strncmp(argv[i], "-R", 2) == 0)
525        {
526            if (argv[i][2] == '\0')
527            {
528                if (warningLevel > 0)
529                {
530                    WARN("No root directory specified\n");
531                    ACTION("Ignoring -R option\n");
532                }
533            }
534            else if (rootDir != NULL)
535            {
536                if (warningLevel > 0)
537                {
538                    WARN("Multiple root directories specified\n");
539                    ACTION("Using %s, ignoring %s\n", rootDir, argv[i]);
540                }
541            }
542            else
543            {
544                rootDir = &argv[i][2];
545                if (warningLevel > 8)
546                {
547                    WARN("Changing root directory to \"%s\"\n", rootDir);
548                }
549                if (chdir(rootDir) == 0)
550                {
551                    XkbAddDirectoryToPath(".");
552                } else if (warningLevel > 0)
553                {
554                       WARN("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            const 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                    ERROR("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                    ACTION("\"%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                    ACTION("\"%s\" flag ignored\n", argv[i]);
635                }
636            }
637            else
638                outputFormat = WANT_XKM_FILE;
639        }
640        else
641        {
642            ERROR("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 (strcmp(inputFile, "-") == 0)
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                    ACTION("map from name (\"%s\") ignored\n", tmpstr);
692                }
693            }
694            else
695            {
696                ERROR("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) && (strcmp(inputFile, "-") == 0))
759    {
760#ifdef HAVE_ASPRINTF
761        if (asprintf(&outputFile, "stdin.%s", fileTypeExt[outputFormat]) < 0)
762#else
763        size_t len = strlen("stdin") + strlen(fileTypeExt[outputFormat]) + 2;
764        outputFile = calloc(len, sizeof(char));
765        if (outputFile != NULL)
766            snprintf(outputFile, len, "stdin.%s", fileTypeExt[outputFormat]);
767        else
768#endif
769        {
770            WSGO("Cannot allocate space for output file name\n");
771            ACTION("Exiting\n");
772            exit(1);
773        }
774    }
775    else if ((outputFile == NULL) && (inputFile != NULL))
776    {
777        int len;
778        const char *base, *ext;
779
780        if (inputMap == NULL)
781        {
782            base = strrchr(inputFile, '/');
783            if (base == NULL)
784                base = inputFile;
785            else
786                base++;
787        }
788        else
789            base = inputMap;
790
791        len = strlen(base) + strlen(fileTypeExt[outputFormat]) + 2;
792        outputFile = calloc(len, sizeof(char));
793        if (outputFile == NULL)
794        {
795            WSGO("Cannot allocate space for output file name\n");
796            ACTION("Exiting\n");
797            exit(1);
798        }
799        ext = strrchr(base, '.');
800        if (ext == NULL)
801            snprintf(outputFile, len, "%s.%s", base, fileTypeExt[outputFormat]);
802        else
803        {
804            strcpy(outputFile, base);
805            strcpy(&outputFile[ext - base + 1], fileTypeExt[outputFormat]);
806        }
807    }
808    else if (outputFile == NULL)
809    {
810        int len;
811        char *ch, buf[128];
812        const char *name = buf;
813        if (inDpyName[0] == ':')
814            snprintf(buf, sizeof(buf), "server%s", inDpyName);
815        else
816            name = inDpyName;
817
818        len = strlen(name) + strlen(fileTypeExt[outputFormat]) + 2;
819        outputFile = calloc(len, sizeof(char));
820        if (outputFile == NULL)
821        {
822            WSGO("Cannot allocate space for output file name\n");
823            ACTION("Exiting\n");
824            exit(1);
825        }
826        strcpy(outputFile, name);
827        for (ch = outputFile; (*ch) != '\0'; ch++)
828        {
829            if (*ch == ':')
830                *ch = '-';
831            else if (*ch == '.')
832                *ch = '_';
833        }
834        *ch++ = '.';
835        strcpy(ch, fileTypeExt[outputFormat]);
836    }
837#ifdef WIN32
838    else if (strlen(outputFile) > 2 &&
839             isalpha(outputFile[0]) &&
840             outputFile[1] == ':' && strchr(outputFile + 2, ':') == NULL)
841    {
842    }
843#endif
844    else if (strchr(outputFile, ':') != NULL)
845    {
846        outDpyName = outputFile;
847        outputFile = NULL;
848        outputFormat = WANT_X_SERVER;
849    }
850    return True;
851}
852
853static Display *
854GetDisplay(const char *program, const char *dpyName)
855{
856    int mjr, mnr, error;
857    Display *dpy;
858
859    mjr = XkbMajorVersion;
860    mnr = XkbMinorVersion;
861    dpy = XkbOpenDisplay(dpyName, NULL, NULL, &mjr, &mnr, &error);
862    if (dpy == NULL)
863    {
864        switch (error)
865        {
866        case XkbOD_BadLibraryVersion:
867            INFO("%s was compiled with XKB version %d.%02d\n",
868                  program, XkbMajorVersion, XkbMinorVersion);
869            ERROR("X library supports incompatible version %d.%02d\n",
870                   mjr, mnr);
871            break;
872        case XkbOD_ConnectionRefused:
873            ERROR("Cannot open display \"%s\"\n", dpyName);
874            break;
875        case XkbOD_NonXkbServer:
876            ERROR("XKB extension not present on %s\n", dpyName);
877            break;
878        case XkbOD_BadServerVersion:
879            INFO("%s was compiled with XKB version %d.%02d\n",
880                  program, XkbMajorVersion, XkbMinorVersion);
881            ERROR("Server %s uses incompatible version %d.%02d\n",
882                   dpyName, mjr, mnr);
883            break;
884        default:
885            WSGO("Unknown error %d from XkbOpenDisplay\n", error);
886        }
887    }
888    else if (synch)
889        XSynchronize(dpy, True);
890    return dpy;
891}
892
893/***====================================================================***/
894
895#ifdef DEBUG
896extern int yydebug;
897#endif
898
899int
900main(int argc, char *argv[])
901{
902    FILE *file;         /* input file (or stdin) */
903    XkbFile *rtrn;
904    XkbFile *mapToUse;
905    int ok;
906    XkbFileInfo result;
907    Status status;
908
909    scan_set_file(stdin);
910#ifdef DEBUG
911    uSetDebugFile(NullString);
912#endif
913    uSetErrorFile(NullString);
914
915    XkbInitIncludePath();
916    if (!parseArgs(argc, argv))
917        exit(1);
918#ifdef DEBUG
919    if (debugFlags & 0x2)
920        yydebug = 1;
921#endif
922    if (preErrorMsg)
923        uSetPreErrorMessage(preErrorMsg);
924    if (errorPrefix)
925        uSetErrorPrefix(errorPrefix);
926    if (postErrorMsg)
927        uSetPostErrorMessage(postErrorMsg);
928    file = NULL;
929    XkbInitAtoms(NULL);
930    XkbAddDefaultDirectoriesToPath();
931    if (xkblist)
932    {
933        Bool gotSome;
934        gotSome = GenerateListing(outputFile);
935        if ((warningLevel > 7) && (!gotSome))
936            return -1;
937        return 0;
938    }
939    if (inputFile != NULL)
940    {
941        if (strcmp(inputFile, "-") == 0)
942        {
943            file = stdin;
944            inputFile = "stdin";
945        }
946        else
947        {
948            file = fopen(inputFile, "r");
949        }
950    }
951    else if (inDpyName != NULL)
952    {
953        inDpy = GetDisplay(argv[0], inDpyName);
954        if (!inDpy)
955        {
956            ACTION("Exiting\n");
957            exit(1);
958        }
959    }
960    if (outDpyName != NULL)
961    {
962        outDpy = GetDisplay(argv[0], outDpyName);
963        if (!outDpy)
964        {
965            ACTION("Exiting\n");
966            exit(1);
967        }
968    }
969    if ((inDpy == NULL) && (outDpy == NULL))
970    {
971        int mjr, mnr;
972        mjr = XkbMajorVersion;
973        mnr = XkbMinorVersion;
974        if (!XkbLibraryVersion(&mjr, &mnr))
975        {
976            INFO("%s was compiled with XKB version %d.%02d\n",
977                  argv[0], XkbMajorVersion, XkbMinorVersion);
978            ERROR("X library supports incompatible version %d.%02d\n",
979                   mjr, mnr);
980            ACTION("Exiting\n");
981            exit(1);
982        }
983    }
984    if (file)
985    {
986        ok = True;
987        setScanState(inputFile, 1);
988        if ((inputFormat == INPUT_XKB) /* parse .xkb file */
989            && (XKBParseFile(file, &rtrn) && (rtrn != NULL)))
990        {
991            fclose(file);
992            mapToUse = rtrn;
993            if (inputMap != NULL) /* map specified on cmdline? */
994            {
995                while ((mapToUse)
996                       && (!uStringEqual(mapToUse->name, inputMap)))
997                {
998                    mapToUse = (XkbFile *) mapToUse->common.next;
999                }
1000                if (!mapToUse)
1001                {
1002                    FATAL("No map named \"%s\" in \"%s\"\n",
1003                           inputMap, inputFile);
1004                    /* NOTREACHED */
1005                }
1006            }
1007            else if (rtrn->common.next != NULL)
1008            {
1009                /* look for map with XkbLC_Default flag. */
1010                mapToUse = rtrn;
1011                for (; mapToUse; mapToUse = (XkbFile *) mapToUse->common.next)
1012                {
1013                    if (mapToUse->flags & XkbLC_Default)
1014                        break;
1015                }
1016                if (!mapToUse)
1017                {
1018                    mapToUse = rtrn;
1019                    if (warningLevel > 4)
1020                    {
1021                        WARN
1022                            ("No map specified, but \"%s\" has several\n",
1023                             inputFile);
1024                        ACTION
1025                            ("Using the first defined map, \"%s\"\n",
1026                             mapToUse->name);
1027                    }
1028                }
1029            }
1030            bzero(&result, sizeof(result));
1031            result.type = mapToUse->type;
1032            if ((result.xkb = XkbAllocKeyboard()) == NULL)
1033            {
1034                WSGO("Cannot allocate keyboard description\n");
1035                /* NOTREACHED */
1036            }
1037            switch (mapToUse->type)
1038            {
1039            case XkmSemanticsFile:
1040            case XkmLayoutFile:
1041            case XkmKeymapFile:
1042                ok = CompileKeymap(mapToUse, &result, MergeReplace);
1043                break;
1044            case XkmKeyNamesIndex:
1045                ok = CompileKeycodes(mapToUse, &result, MergeReplace);
1046                break;
1047            case XkmTypesIndex:
1048                ok = CompileKeyTypes(mapToUse, &result, MergeReplace);
1049                break;
1050            case XkmSymbolsIndex:
1051                /* if it's just symbols, invent key names */
1052                result.xkb->flags |= AutoKeyNames;
1053                ok = False;
1054                break;
1055            case XkmCompatMapIndex:
1056                ok = CompileCompatMap(mapToUse, &result, MergeReplace, NULL);
1057                break;
1058            case XkmGeometryFile:
1059            case XkmGeometryIndex:
1060                /* if it's just a geometry, invent key names */
1061                result.xkb->flags |= AutoKeyNames;
1062                ok = CompileGeometry(mapToUse, &result, MergeReplace);
1063                break;
1064            default:
1065                WSGO("Unknown file type %d\n", mapToUse->type);
1066                ok = False;
1067                break;
1068            }
1069            result.xkb->device_spec = device_id;
1070        }
1071        else if (inputFormat == INPUT_XKM) /* parse xkm file */
1072        {
1073            unsigned tmp;
1074            bzero(&result, sizeof(result));
1075            if ((result.xkb = XkbAllocKeyboard()) == NULL)
1076            {
1077                WSGO("Cannot allocate keyboard description\n");
1078                /* NOTREACHED */
1079            }
1080            tmp = XkmReadFile(file, 0, XkmKeymapLegal, &result);
1081            if (tmp == XkmKeymapLegal)
1082            {
1083                ERROR("Cannot read XKM file \"%s\"\n", inputFile);
1084                ok = False;
1085            }
1086            result.xkb->device_spec = device_id;
1087        }
1088        else
1089        {
1090            INFO("Errors encountered in %s; not compiled.\n", inputFile);
1091            ok = False;
1092        }
1093    }
1094    else if (inDpy != NULL)
1095    {
1096        bzero(&result, sizeof(result));
1097        result.type = XkmKeymapFile;
1098        result.xkb = XkbGetMap(inDpy, XkbAllMapComponentsMask, device_id);
1099        if (result.xkb == NULL)
1100            WSGO("Cannot load keyboard description\n");
1101        if (XkbGetIndicatorMap(inDpy, ~0, result.xkb) != Success)
1102            WSGO("Could not load indicator map\n");
1103        if (XkbGetControls(inDpy, XkbAllControlsMask, result.xkb) != Success)
1104            WSGO("Could not load keyboard controls\n");
1105        if (XkbGetCompatMap(inDpy, XkbAllCompatMask, result.xkb) != Success)
1106            WSGO("Could not load compatibility map\n");
1107        if (XkbGetNames(inDpy, XkbAllNamesMask, result.xkb) != Success)
1108            WSGO("Could not load names\n");
1109        if ((status = XkbGetGeometry(inDpy, result.xkb)) != Success)
1110        {
1111            if (warningLevel > 3)
1112            {
1113                char buf[100];
1114                buf[0] = '\0';
1115                XGetErrorText(inDpy, status, buf, 100);
1116                WARN("Could not load keyboard geometry for %s\n", inDpyName);
1117                ACTION("%s\n", buf);
1118                ACTION("Resulting keymap file will not describe geometry\n");
1119            }
1120        }
1121        if (computeDflts)
1122            ok = (ComputeKbdDefaults(result.xkb) == Success);
1123        else
1124            ok = True;
1125    }
1126    else
1127    {
1128        fprintf(stderr, "Cannot open \"%s\" to compile\n", inputFile);
1129        ok = 0;
1130    }
1131    if (ok)
1132    {
1133        FILE *out = stdout;
1134        if ((inDpy != outDpy) &&
1135            (XkbChangeKbdDisplay(outDpy, &result) != Success))
1136        {
1137            WSGO("Error converting keyboard display from %s to %s\n",
1138                  inDpyName, outDpyName);
1139            exit(1);
1140        }
1141        if (outputFile != NULL)
1142        {
1143            if (strcmp(outputFile, "-") == 0)
1144                outputFile = "stdout";
1145            else
1146            {
1147                /*
1148                 * fix to prevent symlink attack (e.g.,
1149                 * ln -s /etc/passwd /var/tmp/server-0.xkm)
1150                 */
1151                /*
1152                 * this patch may have POSIX, Linux, or GNU libc bias
1153                 * -- Branden Robinson
1154                 */
1155                int outputFileFd;
1156                int binMode = 0;
1157                const char *openMode = "w";
1158                unlink(outputFile);
1159#ifdef O_BINARY
1160                switch (outputFormat)
1161                {
1162                case WANT_XKM_FILE:
1163                    binMode = O_BINARY;
1164                    openMode = "wb";
1165                    break;
1166                default:
1167                    binMode = 0;
1168                    break;
1169                }
1170#endif
1171                outputFileFd =
1172                    open(outputFile, O_WRONLY | O_CREAT | O_EXCL,
1173                         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
1174                         | S_IWOTH | binMode);
1175                if (outputFileFd < 0)
1176                {
1177                    ERROR
1178                        ("Cannot open \"%s\" to write keyboard description\n",
1179                         outputFile);
1180                    ACTION("Exiting\n");
1181                    exit(1);
1182                }
1183#ifndef WIN32
1184                out = fdopen(outputFileFd, openMode);
1185#else
1186                close(outputFileFd);
1187                out = fopen(outputFile, "wb");
1188#endif
1189                /* end BR */
1190                if (out == NULL)
1191                {
1192                    ERROR
1193                        ("Cannot open \"%s\" to write keyboard description\n",
1194                         outputFile);
1195                    ACTION("Exiting\n");
1196                    exit(1);
1197                }
1198            }
1199        }
1200        switch (outputFormat)
1201        {
1202        case WANT_XKM_FILE:
1203            ok = XkbWriteXKMFile(out, &result);
1204            break;
1205        case WANT_XKB_FILE:
1206            ok = XkbWriteXKBFile(out, &result, showImplicit, NULL, NULL);
1207            break;
1208        case WANT_C_HDR:
1209            ok = XkbWriteCFile(out, outputFile, &result);
1210            break;
1211        case WANT_X_SERVER:
1212            if (!(ok = XkbWriteToServer(&result)))
1213            {
1214                ERROR("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1215                       _XkbErrLocation ? _XkbErrLocation : "unknown");
1216                ACTION("Couldn't write keyboard description to %s\n",
1217                        outDpyName);
1218            }
1219            break;
1220        default:
1221            WSGO("Unknown output format %d\n", outputFormat);
1222            ACTION("No output file created\n");
1223            ok = False;
1224            break;
1225        }
1226        if (outputFormat != WANT_X_SERVER)
1227        {
1228            if (fclose(out))
1229            {
1230                ERROR("Cannot close \"%s\" properly (not enough space?)\n",
1231                       outputFile);
1232                ok= False;
1233            }
1234            else if (!ok)
1235            {
1236                ERROR("%s in %s\n", _XkbErrMessages[_XkbErrCode],
1237                       _XkbErrLocation ? _XkbErrLocation : "unknown");
1238            }
1239            if (!ok)
1240            {
1241                ACTION("Output file \"%s\" removed\n", outputFile);
1242                unlink(outputFile);
1243            }
1244        }
1245    }
1246    if (inDpy)
1247        XCloseDisplay(inDpy);
1248    inDpy = NULL;
1249    if (outDpy)
1250        XCloseDisplay(outDpy);
1251    uFinishUp();
1252    return (ok == 0);
1253}
1254