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