xkbcomp.c revision f46a6179
1/* $Xorg: xkbcomp.c,v 1.4 2000/08/17 19:54:33 cpqbld Exp $ */
2/************************************************************
3 Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
4
5 Permission to use, copy, modify, and distribute this
6 software and its documentation for any purpose and without
7 fee is hereby granted, provided that the above copyright
8 notice appear in all copies and that both that copyright
9 notice and this permission notice appear in supporting
10 documentation, and that the name of Silicon Graphics not be
11 used in advertising or publicity pertaining to distribution
12 of the software without specific prior written permission.
13 Silicon Graphics makes no representation about the suitability
14 of this software for any purpose. It is provided "as is"
15 without any express or implied warranty.
16
17 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
18 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
19 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
20 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
21 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
23 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
24 THE USE OR PERFORMANCE OF THIS SOFTWARE.
25
26 ********************************************************/
27/* $XFree86: xc/programs/xkbcomp/xkbcomp.c,v 3.20 2003/09/24 02:43:38 dawes Exp $ */
28
29#include <stdio.h>
30#include <ctype.h>
31#include <X11/keysym.h>
32
33/* for symlink attack security fix -- Branden Robinson */
34#include <sys/stat.h>
35#include <sys/types.h>
36#include <unistd.h>
37/* end BR */
38
39#if defined(sgi)
40#include <malloc.h>
41#endif
42
43#define	DEBUG_VAR_NOT_LOCAL
44#define	DEBUG_VAR debugFlags
45#include "xkbcomp.h"
46#include <stdlib.h>
47#include "xkbpath.h"
48#include "parseutils.h"
49#include "misc.h"
50#include "tokens.h"
51#include <X11/extensions/XKBgeom.h>
52
53#ifdef __UNIXOS2__
54#define chdir _chdir2
55#endif
56
57#ifdef WIN32
58#define S_IRGRP 0
59#define S_IWGRP 0
60#define S_IROTH 0
61#define S_IWOTH 0
62#endif
63
64#define	lowbit(x)	((x) & (-(x)))
65
66/***====================================================================***/
67
68#define	WANT_DEFAULT	0
69#define	WANT_XKM_FILE	1
70#define	WANT_C_HDR	2
71#define	WANT_XKB_FILE	3
72#define	WANT_X_SERVER	4
73#define	WANT_LISTING	5
74
75#define	INPUT_UNKNOWN	0
76#define	INPUT_XKB	1
77#define	INPUT_XKM	2
78
79static const char *fileTypeExt[] = {
80	"XXX",
81	"xkm",
82	"h",
83	"xkb",
84	"dir"
85};
86
87static	unsigned	inputFormat,outputFormat;
88	char *		rootDir;
89static	char *		inputFile;
90static	char *		inputMap;
91static	char *		outputFile;
92static	char *		inDpyName;
93static	char *		outDpyName;
94static	Display *	inDpy;
95static	Display *	outDpy;
96static	Bool		showImplicit= False;
97static	Bool		synch= False;
98static	Bool		computeDflts= False;
99static	Bool		xkblist= False;
100	unsigned	warningLevel= 5;
101	unsigned	verboseLevel= 0;
102	unsigned	dirsToStrip= 0;
103	unsigned	optionalParts= 0;
104static	char *		preErrorMsg= NULL;
105static	char *		postErrorMsg= NULL;
106static	char *		errorPrefix= NULL;
107static unsigned int     device_id = XkbUseCoreKbd;
108
109/***====================================================================***/
110
111#define	M(m)	fprintf(stderr,(m))
112#define	M1(m,a)	fprintf(stderr,(m),(a))
113
114static void
115Usage(int argc,char *argv[])
116{
117    if (!xkblist)
118	 M1("Usage: %s [options] input-file [ output-file ]\n",argv[0]);
119    else M1("Usage: %s [options] file[(map)] ...\n",argv[0]);
120    M("Legal options:\n");
121    M("-?,-help             Print this message\n");
122    if (!xkblist) {
123	M("-a                   Show all actions\n");
124	M("-C                   Create a C header file\n");
125    }
126#ifdef DEBUG
127    M("-d [flags]           Report debugging information\n");
128#endif
129    M("-em1 <msg>           Print <msg> before printing first error message\n");
130    M("-emp <msg>           Print <msg> at the start of each message line\n");
131    M("-eml <msg>           If there were any errors, print <msg> before exiting\n");
132    if (!xkblist) {
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	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	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	M("-v [<flags>]         Set level of detail for listing.\n");
164	M("                     flags are as for the -l option\n");
165    }
166    M("-w [<lvl>]           Set warning level (0=none, 10=all)\n");
167    if (!xkblist) {
168	M("-xkb                 Create an XKB source (.xkb) file\n");
169	M("-xkm                 Create a compiled key map (.xkm) file\n");
170    }
171    return;
172}
173
174/***====================================================================***/
175
176static void
177setVerboseFlags(char *str)
178{
179    for (;*str;str++) {
180	switch (*str) {
181	    case 'f': verboseLevel|= WantFullNames; break;
182	    case 'h': verboseLevel|= WantHiddenMaps; break;
183	    case 'l': verboseLevel|= WantLongListing; break;
184	    case 'p': verboseLevel|= WantPartialMaps; break;
185	    case 'R': verboseLevel|= ListRecursive; break;
186	    default:
187		if (warningLevel>4) {
188		    WARN1("Unknown verbose option \"%c\"\n",(unsigned int)*str);
189		    ACTION("Ignored\n");
190		}
191		break;
192	}
193    }
194    return;
195}
196
197static Bool
198parseArgs(int argc,char *argv[])
199{
200register int i,tmp;
201
202    i= strlen(argv[0]);
203    tmp= strlen("xkblist");
204    if ((i>=tmp)&&(strcmp(&argv[0][i-tmp],"xkblist")==0)) {
205	xkblist= True;
206    }
207    for (i=1;i<argc;i++) {
208	int itmp;
209	if ((argv[i][0]!='-')||(uStringEqual(argv[i],"-"))) {
210	    if (!xkblist) {
211		if (inputFile==NULL)
212		    inputFile= argv[i];
213		else if (outputFile==NULL)
214		    outputFile= argv[i];
215		else if (warningLevel>0) {
216		    WARN("Too many file names on command line\n");
217		    ACTION3("Compiling %s, writing to %s, ignoring %s\n",
218					inputFile,outputFile,argv[i]);
219		}
220	    }
221	    else if (!AddMatchingFiles(argv[i]))
222		return False;
223	}
224	else if ((strcmp(argv[i],"-?")==0)||(strcmp(argv[i],"-help")==0)) {
225	    Usage(argc,argv);
226	    exit(0);
227	}
228	else if ((strcmp(argv[i],"-a")==0)&&(!xkblist)) {
229	    showImplicit= True;
230	}
231	else if ((strcmp(argv[i],"-C")==0)&&(!xkblist)) {
232	    if ((outputFormat!=WANT_DEFAULT)&&(outputFormat!=WANT_C_HDR)) {
233		if (warningLevel>0) {
234		    WARN("Multiple output file formats specified\n");
235		    ACTION1("\"%s\" flag ignored\n",argv[i]);
236		}
237	    }
238	    else outputFormat= WANT_C_HDR;
239	}
240#ifdef DEBUG
241	else if (strcmp(argv[i],"-d")==0) {
242	    if ((i>=(argc-1))||(!isdigit(argv[i+1][0]))) {
243		debugFlags= 1;
244	    }
245	    else {
246		if (sscanf(argv[++i],"%i",&itmp) == 1)
247		    debugFlags = itmp;
248	    }
249	    INFO1("Setting debug flags to %d\n",debugFlags);
250	}
251#endif
252	else if ((strcmp(argv[i],"-dflts")==0)&&(!xkblist)) {
253	    computeDflts= True;
254	}
255	else if (strcmp(argv[i],"-em1")==0) {
256	    if (++i>=argc) {
257		if (warningLevel>0) {
258		    WARN("No pre-error message specified\n");
259		    ACTION("Trailing \"-em1\" option ignored\n");
260		}
261	    }
262	    else if (preErrorMsg!=NULL) {
263		if (warningLevel>0) {
264		    WARN("Multiple pre-error messsages specified\n");
265		    ACTION2("Compiling %s, ignoring %s\n",preErrorMsg,argv[i]);
266	 	}
267	    }
268	    else preErrorMsg= argv[i];
269	}
270	else if (strcmp(argv[i],"-emp")==0) {
271	    if (++i>=argc) {
272		if (warningLevel>0) {
273		    WARN("No error prefix specified\n");
274		    ACTION("Trailing \"-emp\" option ignored\n");
275		}
276	    }
277	    else if (errorPrefix!=NULL) {
278		if (warningLevel>0) {
279		    WARN("Multiple error prefixes specified\n");
280		    ACTION2("Compiling %s, ignoring %s\n",errorPrefix,argv[i]);
281	 	}
282	    }
283	    else errorPrefix= argv[i];
284	}
285	else if (strcmp(argv[i],"-eml")==0) {
286	    if (++i>=argc) {
287		if (warningLevel>0) {
288		    WARN("No post-error message specified\n");
289		    ACTION("Trailing \"-eml\" option ignored\n");
290		}
291	    }
292	    else if (postErrorMsg!=NULL) {
293		if (warningLevel>0) {
294		    WARN("Multiple post-error messages specified\n");
295		    ACTION2("Compiling %s, ignoring %s\n",postErrorMsg,argv[i]);
296	 	}
297	    }
298	    else postErrorMsg= argv[i];
299	}
300	else if ((strncmp(argv[i],"-I",2)==0)&&(!xkblist)) {
301	    if (!XkbAddDirectoryToPath(&argv[i][2])) {
302		ACTION("Exiting\n");
303		exit(1);
304	    }
305	}
306        else if ((strncmp(argv[i], "-i", 2) == 0) && (!xkblist)) {
307            if (++i >= argc) {
308                if (warningLevel > 0)
309                    WARN("No device ID specified\n");
310            }
311            device_id = atoi(argv[i]);
312        }
313	else if ((strncmp(argv[i],"-l",2)==0)&&(!xkblist)) {
314	    if (outputFormat!=WANT_DEFAULT) {
315		if (warningLevel>0) {
316		    WARN("Multiple output file formats specified\n");
317		    ACTION1("\"%s\" flag ignored\n",argv[i]);
318		}
319	    }
320	    else {
321		if (argv[i][2]!='\0')
322		    setVerboseFlags(&argv[i][2]);
323		xkblist= True;
324		if ((inputFile)&&(!AddMatchingFiles(inputFile)))
325		     return False;
326		else inputFile= NULL;
327		if ((outputFile)&&(!AddMatchingFiles(outputFile)))
328		     return False;
329		else outputFile= NULL;
330	    }
331	}
332	else if ((strcmp(argv[i],"-m")==0)||(strcmp(argv[i],"-map")==0)) {
333	    if (++i>=argc) {
334		if (warningLevel>0) {
335		    WARN("No map name specified\n");
336		    ACTION1("Trailing \"%s\" option ignored\n",argv[i-1]);
337		}
338	    }
339	    else if (xkblist) {
340		 if (!AddMapOnly(argv[i]))
341		    return False;
342	    }
343	    else if (inputMap!=NULL) {
344		if (warningLevel>0) {
345		    WARN("Multiple map names specified\n");
346		    ACTION2("Compiling %s, ignoring %s\n",inputMap,argv[i]);
347	 	}
348	    }
349	    else inputMap= argv[i];
350	}
351	else if ((strcmp(argv[i],"-merge")==0)&&(!xkblist)) {
352	    /* Ignored */
353	}
354	else if (strcmp(argv[i],"-o")==0) {
355	    if (++i>=argc) {
356		if (warningLevel>0) {
357		    WARN("No output file specified\n");
358		    ACTION("Trailing \"-o\" option ignored\n");
359		}
360	    }
361	    else if (outputFile!=NULL) {
362		if (warningLevel>0) {
363		    WARN("Multiple output files specified\n");
364		    ACTION2("Compiling %s, ignoring %s\n",outputFile,argv[i]);
365		}
366	    }
367	    else outputFile= argv[i];
368	}
369	else if (((strcmp(argv[i],"-opt")==0)||(strcmp(argv[i],"optional")==0))
370		 						&&(!xkblist)) {
371	    if (++i>=argc) {
372		if (warningLevel>0) {
373		    WARN("No optional components specified\n");
374		    ACTION1("Trailing \"%s\" option ignored\n",argv[i-1]);
375		}
376	    }
377	    else {
378		char *tmp2;
379		for (tmp2=argv[i];(*tmp2!='\0');tmp2++) {
380		    switch (*tmp2) {
381			case 'c': case 'C':
382			    optionalParts|= XkmCompatMapMask;
383			    break;
384			case 'g': case 'G':
385			    optionalParts|= XkmGeometryMask;
386			    break;
387			case 'k': case 'K':
388			    optionalParts|= XkmKeyNamesMask;
389			    break;
390			case 's': case 'S':
391			    optionalParts|= XkmSymbolsMask;
392			    break;
393			case 't': case 'T':
394			    optionalParts|= XkmTypesMask;
395			    break;
396			default:
397			    if (warningLevel>0) {
398				WARN1("Illegal component for %s option\n",
399								argv[i-1]);
400				ACTION1("Ignoring unknown specifier \"%c\"\n",
401								(unsigned int)*tmp2);
402			    }
403			    break;
404		    }
405		}
406	    }
407	}
408	else if (strncmp(argv[i],"-p",2)==0) {
409	    if (isdigit(argv[i][2])) {
410		if (sscanf(&argv[i][2],"%i",&itmp) == 1)
411		    dirsToStrip = itmp;
412	    }
413	    else if ((i<(argc-1))&&(isdigit(argv[i+1][0]))) {
414		if (sscanf(argv[++i],"%i",&itmp) == 1)
415		    dirsToStrip = itmp;
416	    }
417	    else {
418		dirsToStrip= 0;
419	    }
420	    if (warningLevel>5)
421		INFO1("Setting path count to %d\n",dirsToStrip);
422	}
423	else if (strncmp(argv[i],"-R",2)==0) {
424	    if (argv[i][2]=='\0') {
425		if (warningLevel>0) {
426		    WARN("No root directory specified\n");
427		    ACTION("Ignoring -R option\n");
428		}
429	    }
430	    else if (rootDir!=NULL) {
431		if (warningLevel>0) {
432		    WARN("Multiple root directories specified\n");
433		    ACTION2("Using %s, ignoring %s\n",rootDir,argv[i]);
434		}
435	    }
436	    else {
437		rootDir= &argv[i][2];
438		if (warningLevel>8) {
439		    WARN1("Changing root directory to \"%s\"\n",rootDir);
440		}
441		if ((chdir(rootDir)<0) && (warningLevel>0)) {
442		    WARN1("Couldn't change directory to \"%s\"\n",rootDir);
443		    ACTION("Root directory (-R) option ignored\n");
444		    rootDir= NULL;
445		}
446	    }
447	}
448	else if ((strcmp(argv[i],"-synch")==0)||(strcmp(argv[i],"-s")==0)) {
449	    synch= True;
450	}
451	else if (strncmp(argv[i],"-v",2)==0) {
452	    char *str;
453	    if (argv[i][2]!='\0')
454		 str= &argv[i][2];
455	    else if ((i<(argc-1))&&(argv[i+1][0]!='-'))
456		 str= argv[++i];
457	    else str= NULL;
458	    if (str)
459		setVerboseFlags(str);
460	}
461	else if (strncmp(argv[i],"-w",2)==0) {
462	    if ((i>=(argc-1))||(!isdigit(argv[i+1][0]))) {
463		warningLevel = 0;
464		if (isdigit(argv[i][1]))
465		     if (sscanf(&argv[i][1],"%i",&itmp) == 1)
466			warningLevel = itmp;
467	    }
468	    else {
469		if (sscanf(argv[++i],"%i",&itmp) == 1)
470		    warningLevel = itmp;
471	    }
472	}
473	else if ((strcmp(argv[i],"-xkb")==0)&&(!xkblist)) {
474	    if ((outputFormat!=WANT_DEFAULT)&&(outputFormat!=WANT_XKB_FILE)) {
475		if (warningLevel>0) {
476		    WARN("Multiple output file formats specified\n");
477		    ACTION1("\"%s\" flag ignored\n",argv[i]);
478		}
479	    }
480	    else outputFormat= WANT_XKB_FILE;
481	}
482	else if ((strcmp(argv[i],"-xkm")==0)&&(!xkblist)) {
483	    if ((outputFormat!=WANT_DEFAULT)&&(outputFormat!=WANT_XKM_FILE)) {
484		if (warningLevel>0) {
485		    WARN("Multiple output file formats specified\n");
486		    ACTION1("\"%s\" flag ignored\n",argv[i]);
487		}
488	    }
489	    else outputFormat= WANT_XKM_FILE;
490	}
491	else {
492	    ERROR1("Unknown flag \"%s\" on command line\n",argv[i]);
493	    Usage(argc,argv);
494	    return False;
495	}
496    }
497    if (xkblist)
498	inputFormat= INPUT_XKB;
499    else if (inputFile==NULL) {
500	ERROR("No input file specified\n");
501	return False;
502    }
503    else if (uStringEqual(inputFile,"-")) {
504	inputFormat= INPUT_XKB;
505    }
506#ifndef WIN32
507    else if (strchr(inputFile,':')==0) {
508#else
509    else if ((strchr(inputFile,':')==0) || (
510            strlen(inputFile) > 2 &&
511            isalpha(inputFile[0]) &&
512            inputFile[1] == ':' && strchr(inputFile + 2,':')==NULL)) {
513#endif
514	int	len;
515	len= strlen(inputFile);
516	if (inputFile[len-1]==')') {
517	    char *tmp;
518	    if ((tmp=strchr(inputFile,'('))!=0) {
519		*tmp= '\0';  inputFile[len-1]= '\0';
520		tmp++;
521		if (*tmp=='\0') {
522		     WARN("Empty map in filename\n");
523		     ACTION("Ignored\n");
524		}
525		else if (inputMap==NULL) {
526		    inputMap= uStringDup(tmp);
527		}
528		else {
529		    WARN("Map specified in filename and with -m flag\n");
530		    ACTION1("map from name (\"%s\") ignored\n",tmp);
531		}
532	    }
533	    else {
534		ERROR1("Illegal name \"%s\" for input file\n",inputFile);
535		return False;
536	    }
537	}
538	if ((len>4)&&(strcmp(&inputFile[len-4],".xkm")==0)) {
539	    inputFormat= INPUT_XKM;
540	}
541	else {
542	    FILE *file;
543	    file= fopen(inputFile,"r");
544	    if (file) {
545		if (XkmProbe(file))	inputFormat= INPUT_XKM;
546		else			inputFormat= INPUT_XKB;
547		fclose(file);
548	    }
549	    else {
550		fprintf(stderr,"Cannot open \"%s\" for reading\n",inputFile);
551		return False;
552	    }
553	}
554    }
555    else {
556	inDpyName= inputFile;
557	inputFile= NULL;
558	inputFormat= INPUT_XKM;
559    }
560
561    if (outputFormat==WANT_DEFAULT) {
562	if (xkblist)				outputFormat= WANT_LISTING;
563	else if (inputFormat==INPUT_XKB)	outputFormat= WANT_XKM_FILE;
564	else					outputFormat= WANT_XKB_FILE;
565    }
566    if ((outputFormat==WANT_LISTING)&&(inputFormat!=INPUT_XKB)) {
567	if (inputFile)
568	     ERROR("Cannot generate a listing from a .xkm file (yet)\n");
569	else ERROR("Cannot generate a listing from an X connection (yet)\n");
570	return False;
571    }
572    if (xkblist) {
573	if (outputFile==NULL)	outputFile= uStringDup("-");
574	else if (strchr(outputFile,':')!=NULL) {
575	    ERROR("Cannot write a listing to an X connection\n");
576	    return False;
577	}
578    }
579    else if ((!outputFile) && (inputFile) && uStringEqual(inputFile,"-")) {
580	int len= strlen("stdin")+strlen(fileTypeExt[outputFormat])+2;
581	outputFile= uTypedCalloc(len,char);
582	if (outputFile==NULL) {
583	    WSGO("Cannot allocate space for output file name\n");
584	    ACTION("Exiting\n");
585	    exit(1);
586	}
587	sprintf(outputFile,"stdin.%s",fileTypeExt[outputFormat]);
588    }
589    else if ((outputFile==NULL)&&(inputFile!=NULL)) {
590	int len;
591	char *base,*ext;
592
593	if (inputMap==NULL)  {
594	    base= strrchr(inputFile,'/');
595	    if (base==NULL)	base= inputFile;
596	    else		base++;
597	}
598	else base= inputMap;
599
600	len= strlen(base)+strlen(fileTypeExt[outputFormat])+2;
601	outputFile= uTypedCalloc(len,char);
602	if (outputFile==NULL) {
603	    WSGO("Cannot allocate space for output file name\n");
604	    ACTION("Exiting\n");
605	    exit(1);
606	}
607	ext= strrchr(base,'.');
608	if (ext==NULL)
609	    sprintf(outputFile,"%s.%s",base,fileTypeExt[outputFormat]);
610	else {
611	    strcpy(outputFile,base);
612	    strcpy(&outputFile[ext-base+1],fileTypeExt[outputFormat]);
613	}
614    }
615    else if (outputFile==NULL) {
616	int len;
617	char *ch,*name,buf[128];
618	if (inDpyName[0]==':')
619	     sprintf(name=buf,"server%s",inDpyName);
620	else name= inDpyName;
621
622	len= strlen(name)+strlen(fileTypeExt[outputFormat])+2;
623	outputFile= uTypedCalloc(len,char);
624	if (outputFile==NULL) {
625	    WSGO("Cannot allocate space for output file name\n");
626	    ACTION("Exiting\n");
627	    exit(1);
628	}
629	strcpy(outputFile,name);
630	for (ch=outputFile;(*ch)!='\0';ch++) {
631	    if 	(*ch==':')	*ch= '-';
632	    else if (*ch=='.')	*ch= '_';
633	}
634	*ch++= '.';
635	strcpy(ch,fileTypeExt[outputFormat]);
636    }
637#ifdef WIN32
638    else if (strlen(outputFile) > 2 &&
639            isalpha(outputFile[0]) &&
640            outputFile[1] == ':' && strchr(outputFile + 2,':')==NULL) {
641    }
642#endif
643    else if (strchr(outputFile,':')!=NULL) {
644	outDpyName= outputFile;
645	outputFile= NULL;
646	outputFormat=  WANT_X_SERVER;
647    }
648    return True;
649}
650
651static Display *
652GetDisplay(char *program,char *dpyName)
653{
654int	mjr,mnr,error;
655Display	*dpy;
656
657    mjr= XkbMajorVersion;
658    mnr= XkbMinorVersion;
659    dpy= XkbOpenDisplay(dpyName,NULL,NULL,&mjr,&mnr,&error);
660    if (dpy==NULL) {
661	switch (error) {
662	    case XkbOD_BadLibraryVersion:
663		INFO3("%s was compiled with XKB version %d.%02d\n",
664				program,XkbMajorVersion,XkbMinorVersion);
665		ERROR2("X library supports incompatible version %d.%02d\n",
666				mjr,mnr);
667		break;
668	    case XkbOD_ConnectionRefused:
669		ERROR1("Cannot open display \"%s\"\n",dpyName);
670		break;
671	    case XkbOD_NonXkbServer:
672		ERROR1("XKB extension not present on %s\n",dpyName);
673		break;
674	    case XkbOD_BadServerVersion:
675		INFO3("%s was compiled with XKB version %d.%02d\n",
676				program,XkbMajorVersion,XkbMinorVersion);
677		ERROR3("Server %s uses incompatible version %d.%02d\n",
678				dpyName,mjr,mnr);
679		break;
680	    default:
681		WSGO1("Unknown error %d from XkbOpenDisplay\n",error);
682	}
683    }
684    else if (synch)
685	XSynchronize(dpy,True);
686    return dpy;
687}
688
689/***====================================================================***/
690
691extern int yydebug;
692
693int
694main(int argc,char *argv[])
695{
696FILE 	*	file;
697XkbFile	*	rtrn;
698XkbFile	*	mapToUse;
699int		ok;
700XkbFileInfo 	result;
701Status		status;
702
703    yyin = stdin;
704    uSetEntryFile(NullString);
705    uSetDebugFile(NullString);
706    uSetErrorFile(NullString);
707
708    XkbInitIncludePath();
709    if (!parseArgs(argc,argv))
710	exit(1);
711#ifdef DEBUG
712    if (debugFlags&0x2)
713	yydebug= 1;
714#endif
715    if (preErrorMsg)
716	uSetPreErrorMessage(preErrorMsg);
717    if (errorPrefix)
718	uSetErrorPrefix(errorPrefix);
719    if (postErrorMsg)
720	uSetPostErrorMessage(postErrorMsg);
721    file= NULL;
722    XkbInitAtoms(NULL);
723    XkbAddDefaultDirectoriesToPath();
724    if (xkblist) {
725	Bool	gotSome;
726	gotSome= GenerateListing(outputFile);
727	if ((warningLevel>7)&&(!gotSome))
728	    return -1;
729	return 0;
730    }
731    if (inputFile!=NULL) {
732	if (uStringEqual(inputFile,"-")) {
733	    file= stdin;
734	    inputFile= "stdin";
735	}
736	else {
737	    file= fopen(inputFile,"r");
738	}
739    }
740    else if (inDpyName!=NULL) {
741	inDpy= GetDisplay(argv[0],inDpyName);
742	if (!inDpy) {
743	    ACTION("Exiting\n");
744	    exit(1);
745	}
746    }
747    if (outDpyName!=NULL) {
748	outDpy= GetDisplay(argv[0],outDpyName);
749	if (!outDpy) {
750	    ACTION("Exiting\n");
751	    exit(1);
752	}
753    }
754    if ((inDpy==NULL) && (outDpy==NULL)) {
755	int	mjr,mnr;
756	mjr= XkbMajorVersion;
757	mnr= XkbMinorVersion;
758	if (!XkbLibraryVersion(&mjr,&mnr)) {
759	    INFO3("%s was compiled with XKB version %d.%02d\n",
760				argv[0],XkbMajorVersion,XkbMinorVersion);
761	    ERROR2("X library supports incompatible version %d.%02d\n",
762				mjr,mnr);
763	    ACTION("Exiting\n");
764	    exit(1);
765	}
766    }
767    if (file) {
768	ok= True;
769	setScanState(inputFile,1);
770	if ((inputFormat==INPUT_XKB)&&(XKBParseFile(file,&rtrn)&&(rtrn!=NULL))){
771	    fclose(file);
772	    mapToUse= rtrn;
773	    if (inputMap!=NULL) {
774		while ((mapToUse)&&(!uStringEqual(mapToUse->name,inputMap))) {
775		    mapToUse= (XkbFile *)mapToUse->common.next;
776		}
777		if (!mapToUse) {
778		    FATAL2("No map named \"%s\" in \"%s\"\n",inputMap,
779		    						inputFile);
780		    /* NOTREACHED */
781		}
782	    }
783	    else if (rtrn->common.next!=NULL) {
784		mapToUse= rtrn;
785		for (;mapToUse;mapToUse= (XkbFile*)mapToUse->common.next) {
786		    if (mapToUse->flags&XkbLC_Default)
787			break;
788		}
789		if (!mapToUse) {
790		    mapToUse= rtrn;
791		    if (warningLevel>4) {
792			WARN1("No map specified, but \"%s\" has several\n",
793								inputFile);
794			ACTION1("Using the first defined map, \"%s\"\n",
795								mapToUse->name);
796		    }
797		}
798	    }
799	    bzero((char *)&result,sizeof(result));
800	    result.type= mapToUse->type;
801	    if ((result.xkb= XkbAllocKeyboard())==NULL) {
802		WSGO("Cannot allocate keyboard description\n");
803		/* NOTREACHED */
804	    }
805	    switch (mapToUse->type) {
806		case XkmSemanticsFile:
807		case XkmLayoutFile:
808		case XkmKeymapFile:
809		    ok= CompileKeymap(mapToUse,&result,MergeReplace);
810		    break;
811		case XkmKeyNamesIndex:
812		    ok= CompileKeycodes(mapToUse,&result,MergeReplace);
813		    break;
814		case XkmTypesIndex:
815		    ok= CompileKeyTypes(mapToUse,&result,MergeReplace);
816		    break;
817		case XkmSymbolsIndex:
818		    /* if it's just symbols, invent key names */
819		    result.xkb->flags|= AutoKeyNames;
820		    ok= False;
821		    break;
822		case XkmCompatMapIndex:
823		    ok= CompileCompatMap(mapToUse,&result,MergeReplace,NULL);
824		    break;
825		case XkmGeometryFile:
826		case XkmGeometryIndex:
827		    /* if it's just a geometry, invent key names */
828		    result.xkb->flags|= AutoKeyNames;
829		    ok= CompileGeometry(mapToUse,&result,MergeReplace);
830		    break;
831		default:
832		    WSGO1("Unknown file type %d\n",mapToUse->type);
833		    ok= False;
834		    break;
835	    }
836	}
837	else if (inputFormat==INPUT_XKM) {
838	    unsigned tmp;
839	    bzero((char *)&result,sizeof(result));
840	    if ((result.xkb= XkbAllocKeyboard())==NULL) {
841		WSGO("Cannot allocate keyboard description\n");
842		/* NOTREACHED */
843	    }
844	    tmp= XkmReadFile(file,0,XkmKeymapLegal,&result);
845	    if (tmp==XkmKeymapLegal) {
846		ERROR1("Cannot read XKM file \"%s\"\n",inputFile);
847		ok= False;
848	    }
849	}
850	else {
851	    INFO1("Errors encountered in %s; not compiled.\n",inputFile);
852	    ok= False;
853	}
854    }
855    else if (inDpy!=NULL) {
856	bzero((char *)&result,sizeof(result));
857	result.type= XkmKeymapFile;
858	result.xkb= XkbGetMap(inDpy,XkbAllMapComponentsMask,device_id);
859	if (result.xkb==NULL)
860	    WSGO("Cannot load keyboard description\n");
861	if (XkbGetIndicatorMap(inDpy,~0,result.xkb)!=Success)
862	    WSGO("Could not load indicator map\n");
863	if (XkbGetControls(inDpy,XkbAllControlsMask,result.xkb)!=Success)
864	    WSGO("Could not load keyboard controls\n");
865	if (XkbGetCompatMap(inDpy,XkbAllCompatMask,result.xkb)!=Success)
866	    WSGO("Could not load compatibility map\n");
867	if (XkbGetNames(inDpy,XkbAllNamesMask,result.xkb)!=Success)
868	    WSGO("Could not load names\n");
869	if ((status=XkbGetGeometry(inDpy,result.xkb))!=Success) {
870	    if (warningLevel>3) {
871		char buf[100];
872		buf[0]= '\0';
873		XGetErrorText(inDpy,status,buf,100);
874		WARN1("Could not load keyboard geometry for %s\n",inDpyName);
875		ACTION1("%s\n",buf);
876		ACTION("Resulting keymap file will not describe geometry\n");
877	    }
878	}
879	if (computeDflts)
880	     ok= (ComputeKbdDefaults(result.xkb)==Success);
881	else ok= True;
882    }
883    else {
884	fprintf(stderr,"Cannot open \"%s\" to compile\n",inputFile);
885	ok= 0;
886    }
887    if (ok) {
888	FILE *out = stdout;
889	if ((inDpy!=outDpy)&&
890	    (XkbChangeKbdDisplay(outDpy,&result)!=Success)) {
891	    WSGO2("Error converting keyboard display from %s to %s\n",
892	    						inDpyName,outDpyName);
893	    exit(1);
894	}
895	if (outputFile!=NULL) {
896	    if (uStringEqual(outputFile,"-"))
897		outputFile= "stdout";
898	    else {
899		/*
900		 * fix to prevent symlink attack (e.g.,
901		 * ln -s /etc/passwd /var/tmp/server-0.xkm)
902		 */
903		/*
904		 * this patch may have POSIX, Linux, or GNU libc bias
905		 * -- Branden Robinson
906		 */
907		int outputFileFd;
908		int binMode = 0;
909		const char *openMode = "w";
910		unlink(outputFile);
911#ifdef O_BINARY
912		switch (outputFormat) {
913		    case WANT_XKM_FILE:
914		        binMode = O_BINARY;
915			openMode = "wb";
916			break;
917		    default:
918		        binMode = 0;
919			break;
920		}
921#endif
922		outputFileFd= open(outputFile, O_WRONLY|O_CREAT|O_EXCL,
923		    S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH|binMode);
924		if (outputFileFd<0) {
925		    ERROR1("Cannot open \"%s\" to write keyboard description\n",
926								outputFile);
927		    ACTION("Exiting\n");
928		    exit(1);
929		}
930#ifndef WIN32
931		out= fdopen(outputFileFd, openMode);
932#else
933        close(outputFileFd);
934        out= fopen(outputFile, "wb");
935#endif
936		/* end BR */
937		if (out==NULL) {
938		    ERROR1("Cannot open \"%s\" to write keyboard description\n",
939								outputFile);
940		    ACTION("Exiting\n");
941		    exit(1);
942		}
943	    }
944	}
945	switch (outputFormat) {
946	    case WANT_XKM_FILE:
947		ok= XkbWriteXKMFile(out,&result);
948		break;
949	    case WANT_XKB_FILE:
950		ok= XkbWriteXKBFile(out,&result,showImplicit,NULL,NULL);
951		break;
952	    case WANT_C_HDR:
953		ok= XkbWriteCFile(out,outputFile,&result);
954		break;
955	    case WANT_X_SERVER:
956	    	if (!(ok= XkbWriteToServer(&result))) {
957		    ERROR2("%s in %s\n",_XkbErrMessages[_XkbErrCode],
958				_XkbErrLocation?_XkbErrLocation:"unknown");
959		    ACTION1("Couldn't write keyboard description to %s\n",
960								outDpyName);
961		}
962		break;
963	    default:
964		WSGO1("Unknown output format %d\n",outputFormat);
965		ACTION("No output file created\n");
966		ok= False;
967		break;
968	}
969	if (outputFormat!=WANT_X_SERVER) {
970	    fclose(out);
971	    if (!ok) {
972		ERROR2("%s in %s\n",_XkbErrMessages[_XkbErrCode],
973				_XkbErrLocation?_XkbErrLocation:"unknown");
974		ACTION1("Output file \"%s\" removed\n",outputFile);
975		unlink(outputFile);
976	    }
977	}
978    }
979    if (inDpy)
980	XCloseDisplay(inDpy);
981    inDpy= NULL;
982    if (outDpy)
983	XCloseDisplay(outDpy);
984    uFinishUp();
985    return (ok==0);
986}
987