setxkbmap.c revision 1568b75b
1/************************************************************
2 Copyright (c) 1996 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 <stdlib.h>
29#include <locale.h>
30#include <limits.h>
31#include <ctype.h>
32#include <X11/Xlib.h>
33#include <X11/Xos.h>
34#include <X11/XKBlib.h>
35#include <X11/extensions/XKBfile.h>
36#include <X11/extensions/XKBconfig.h>
37#include <X11/extensions/XKBrules.h>
38
39#ifndef PATH_MAX
40#ifdef MAXPATHLEN
41#define PATH_MAX MAXPATHLEN
42#else
43#define PATH_MAX 1024
44#endif
45#endif
46
47#ifndef DFLT_XKB_CONFIG_ROOT
48#define	DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb"
49#endif
50#ifndef DFLT_XKB_RULES_FILE
51#define	DFLT_XKB_RULES_FILE __XKBDEFRULES__
52#endif
53#ifndef DFLT_XKB_LAYOUT
54#define	DFLT_XKB_LAYOUT "us"
55#endif
56#ifndef DFLT_XKB_MODEL
57#define	DFLT_XKB_MODEL "pc105"
58#endif
59
60/* Values used in svSrc to state how a value was obtained. The order of these
61 * is important, the bigger the higher the priority.
62 * e.g. FROM_CONFIG overrides FROM_SERVER */
63#define	UNDEFINED	0
64#define	FROM_SERVER	1       /* retrieved from server at runtime */
65#define	FROM_RULES	2       /* xkb rules file */
66#define	FROM_CONFIG	3       /* command-line specified config file */
67#define	FROM_CMD_LINE	4       /* specified at the cmdline */
68#define	NUM_SOURCES	5
69
70/* Indices used into svSrc, svNValue */
71#define	RULES_NDX	0       /* rules file */
72#define	CONFIG_NDX	1       /* config file (if used) */
73#define	DISPLAY_NDX	2       /* X display name */
74#define	LOCALE_NDX	3       /* machine's locale */
75#define	MODEL_NDX	4
76#define	LAYOUT_NDX	5
77#define	VARIANT_NDX	6
78#define KEYCODES_NDX	7
79#define	TYPES_NDX	8
80#define	COMPAT_NDX	9
81#define	SYMBOLS_NDX	10
82#define	GEOMETRY_NDX	11
83#define	KEYMAP_NDX	12
84#define	NUM_STRING_VALS	13
85
86/***====================================================================***/
87static Bool print = False;
88static Bool synch = False;
89static int verbose = 5;
90
91static Display *dpy;
92
93/**
94 * human-readable versions of FROM_CONFIG, FROM_SERVER, etc. Used for error
95 * reporting.
96 */
97static char *srcName[NUM_SOURCES] = {
98    "undefined", "X server", "rules file", "config file", "command line"
99};
100
101/**
102 * human-readable versions for RULES_NDX, CONFIG_NDX, etc. Used for error
103 * reporting.
104 */
105static char *svName[NUM_STRING_VALS] = {
106    "rules file", "config file", "X display", "locale",
107    "keyboard model", "keyboard layout", "layout variant",
108    "keycodes", "types", "compatibility map", "symbols", "geometry",
109    "keymap"
110};
111/**
112 * Holds the source for each of RULES, CONFIG, DISPLAY, etc.
113 * i.e. if svSrc[LAYOUT_NDX] == FROM_SERVER, then the layout has been fetched
114 * from the server.
115 */
116static int svSrc[NUM_STRING_VALS];
117/**
118 * Holds the value for each of RULES, CONFIG, DISPLAY, etc.
119 */
120static char *svValue[NUM_STRING_VALS];
121
122static XkbConfigRtrnRec cfgResult;
123
124static XkbRF_RulesPtr rules = NULL;
125static XkbRF_VarDefsRec rdefs;
126
127static Bool clearOptions = False;
128static int szOptions = 0;
129static int numOptions = 0;
130static char **options = NULL;
131
132static int szInclPath = 0;
133static int numInclPath = 0;
134static char **inclPath = NULL;
135
136static XkbDescPtr xkb = NULL;
137
138static int deviceSpec = XkbUseCoreKbd;
139
140/***====================================================================***/
141
142#define	streq(s1,s2)	(strcmp(s1,s2)==0)
143#define	strpfx(s1,s2)	(strncmp(s1,s2,strlen(s2))==0)
144
145#define	MSG(s)		printf(s)
146#define	MSG1(s,a)	printf(s,a)
147#define	MSG2(s,a,b)	printf(s,a,b)
148#define	MSG3(s,a,b,c)	printf(s,a,b,c)
149
150#define	VMSG(l,s)	if (verbose>(l)) printf(s)
151#define	VMSG1(l,s,a)	if (verbose>(l)) printf(s,a)
152#define	VMSG2(l,s,a,b)	if (verbose>(l)) printf(s,a,b)
153#define	VMSG3(l,s,a,b,c) if (verbose>(l)) printf(s,a,b,c)
154
155#define	ERR(s)		fprintf(stderr,s)
156#define	ERR1(s,a)	fprintf(stderr,s,a)
157#define	ERR2(s,a,b)	fprintf(stderr,s,a,b)
158#define	ERR3(s,a,b,c)	fprintf(stderr,s,a,b,c)
159
160/***====================================================================***/
161
162Bool addToList(int *sz, int *num, char ***listIn, char *newVal);
163void usage(int argc, char **argv);
164void dumpNames(Bool wantRules, Bool wantCNames);
165void trySetString(int which, char *newVal, int src);
166Bool setOptString(int *arg, int argc, char **argv, int which, int src);
167int parseArgs(int argc, char **argv);
168Bool getDisplay(int argc, char **argv);
169Bool getServerValues(void);
170FILE *findFileInPath(char *name, char *subdir);
171Bool addStringToOptions(char *opt_str, int *sz_opts, int *num_opts,
172                        char ***opts);
173char *stringFromOptions(char *orig, int numNew, char **newOpts);
174Bool applyConfig(char *name);
175Bool applyRules(void);
176Bool applyComponentNames(void);
177void printKeymap(void);
178
179/***====================================================================***/
180
181Bool
182addToList(int *sz, int *num, char ***listIn, char *newVal)
183{
184    register int i;
185    char **list;
186
187    if ((!newVal) || (!newVal[0]))
188    {
189        *num = 0;
190        return True;
191    }
192    list = *listIn;
193    for (i = 0; i < *num; i++)
194    {
195        if (streq(list[i], newVal))
196            return True;
197    }
198    if ((list == NULL) || (*sz < 1))
199    {
200        *num = 0;
201        *sz = 4;
202        list = (char **) calloc(*sz, sizeof(char *));
203        *listIn = list;
204    }
205    else if (*num >= *sz)
206    {
207        *sz *= 2;
208        list = (char **) realloc(list, (*sz) * sizeof(char *));
209        *listIn = list;
210    }
211    if (!list)
212    {
213        ERR("Internal Error! Allocation failure in add to list!\n");
214        ERR("                Exiting.\n");
215        exit(-1);
216    }
217    list[*num] = strdup(newVal);
218    (*num) = (*num) + 1;
219    return True;
220}
221
222/***====================================================================***/
223
224void
225usage(int argc, char **argv)
226{
227    MSG1("Usage: %s [args] [<layout> [<variant> [<option> ... ]]]\n",
228         argv[0]);
229    MSG("Where legal args are:\n");
230    MSG("-?,-help            Print this message\n");
231    MSG("-compat <name>      Specifies compatibility map component name\n");
232    MSG("-config <file>      Specifies configuration file to use\n");
233    MSG("-device <deviceid>  Specifies the device ID to use\n");
234    MSG("-display <dpy>      Specifies display to use\n");
235    MSG("-geometry <name>    Specifies geometry component name\n");
236    MSG("-I[<dir>]           Add <dir> to list of directories to be used\n");
237    MSG("-keycodes <name>    Specifies keycodes component name\n");
238    MSG("-keymap <name>      Specifies name of keymap to load\n");
239    MSG("-layout <name>      Specifies layout used to choose component names\n");
240    MSG("-model <name>       Specifies model used to choose component names\n");
241    MSG("-option <name>      Adds an option used to choose component names\n");
242    MSG("-print              Print a complete xkb_keymap description and exit\n");
243    MSG("-rules <name>       Name of rules file to use\n");
244    MSG("-symbols <name>     Specifies symbols component name\n");
245    MSG("-synch              Synchronize request w/X server\n");
246    MSG("-types <name>       Specifies types component name\n");
247    MSG("-v[erbose] [<lvl>]  Sets verbosity (1..10).  Higher values yield\n");
248    MSG("                    more messages\n");
249    MSG("-variant <name>     Specifies layout variant used to choose component names\n");
250}
251
252void
253dumpNames(Bool wantRules, Bool wantCNames)
254{
255    if (wantRules)
256    {
257        if (svValue[MODEL_NDX])
258            MSG1("model:      %s\n", svValue[MODEL_NDX]);
259        if (svValue[LAYOUT_NDX])
260            MSG1("layout:     %s\n", svValue[LAYOUT_NDX]);
261        if (svValue[VARIANT_NDX])
262            MSG1("variant:    %s\n", svValue[VARIANT_NDX]);
263        if (options)
264        {
265            char *opt_str = stringFromOptions(NULL, numOptions, options);
266            MSG1("options:    %s\n", opt_str);
267            free(opt_str);
268        }
269    }
270    if (wantCNames)
271    {
272        if (svValue[KEYMAP_NDX])
273            MSG1("keymap:     %s\n", svValue[KEYMAP_NDX]);
274        if (svValue[KEYCODES_NDX])
275            MSG1("keycodes:   %s\n", svValue[KEYCODES_NDX]);
276        if (svValue[TYPES_NDX])
277            MSG1("types:      %s\n", svValue[TYPES_NDX]);
278        if (svValue[COMPAT_NDX])
279            MSG1("compat:     %s\n", svValue[COMPAT_NDX]);
280        if (svValue[SYMBOLS_NDX])
281            MSG1("symbols:    %s\n", svValue[SYMBOLS_NDX]);
282        if (svValue[GEOMETRY_NDX])
283            MSG1("geometry:   %s\n", svValue[GEOMETRY_NDX]);
284    }
285    return;
286}
287
288/***====================================================================***/
289
290/**
291 * Set the given string (obtained from src) in the svValue/svSrc globals.
292 * If the given item is already set, it is overridden if the original source
293 * is less significant than the given one.
294 *
295 * @param which What value is it (one of RULES_NDX, CONFIG_NDX, ...)
296 */
297void
298trySetString(int which, char *newVal, int src)
299{
300    if (svValue[which] != NULL)
301    {
302        if (svSrc[which] == src)
303        {
304            VMSG2(0, "Warning! More than one %s from %s\n",
305                  svName[which], srcName[src]);
306            VMSG2(0, "         Using \"%s\", ignoring \"%s\"\n",
307                  svValue[which], newVal);
308            return;
309        }
310        else if (svSrc[which] > src)
311        {
312            VMSG1(5, "Warning! Multiple definitions of %s\n", svName[which]);
313            VMSG2(5, "         Using %s, ignoring %s\n",
314                  srcName[svSrc[which]], srcName[src]);
315            return;
316        }
317    }
318    svSrc[which] = src;
319    svValue[which] = newVal;
320    return;
321}
322
323Bool
324setOptString(int *arg, int argc, char **argv, int which, int src)
325{
326    int ndx;
327    char *opt;
328
329    ndx = *arg;
330    opt = argv[ndx];
331    if (ndx >= argc - 1)
332    {
333        VMSG1(0, "No %s specified on the command line\n", svName[which]);
334        VMSG1(0, "Trailing %s option ignored\n", opt);
335        return True;
336    }
337    ndx++;
338    *arg = ndx;
339    if (svValue[which] != NULL)
340    {
341        if (svSrc[which] == src)
342        {
343            VMSG2(0, "More than one %s on %s\n", svName[which], srcName[src]);
344            VMSG2(0, "Using \"%s\", ignoring \"%s\"\n", svValue[which],
345                  argv[ndx]);
346            return True;
347        }
348        else if (svSrc[which] > src)
349        {
350            VMSG1(5, "Multiple definitions of %s\n", svName[which]);
351            VMSG2(5, "Using %s, ignoring %s\n", srcName[svSrc[which]],
352                  srcName[src]);
353            return True;
354        }
355    }
356    svSrc[which] = src;
357    svValue[which] = argv[ndx];
358    return True;
359}
360
361/***====================================================================***/
362
363/**
364 * Parse commandline arguments.
365 * Return True on success or False if an unrecognized option has been
366 * specified.
367 */
368int
369parseArgs(int argc, char **argv)
370{
371    int i;
372    Bool ok;
373    unsigned present;
374
375    ok = True;
376    addToList(&szInclPath, &numInclPath, &inclPath, ".");
377    addToList(&szInclPath, &numInclPath, &inclPath, DFLT_XKB_CONFIG_ROOT);
378    for (i = 1; (i < argc) && ok; i++)
379    {
380        if (argv[i][0] != '-')
381        {
382            /* Allow a call like "setxkbmap us" to work. Layout is default,
383               if -layout is given, then try parsing variant, then options */
384            if (!svSrc[LAYOUT_NDX])
385                trySetString(LAYOUT_NDX, argv[i], FROM_CMD_LINE);
386            else if (!svSrc[VARIANT_NDX])
387                trySetString(VARIANT_NDX, argv[i], FROM_CMD_LINE);
388            else
389                ok = addToList(&szOptions, &numOptions, &options, argv[i]);
390        }
391        else if (streq(argv[i], "-compat"))
392            ok = setOptString(&i, argc, argv, COMPAT_NDX, FROM_CMD_LINE);
393        else if (streq(argv[i], "-config"))
394            ok = setOptString(&i, argc, argv, CONFIG_NDX, FROM_CMD_LINE);
395        else if (streq(argv[i], "-device"))
396            deviceSpec = atoi(argv[++i]); /* only allow device IDs, not names */
397        else if (streq(argv[i], "-display"))
398            ok = setOptString(&i, argc, argv, DISPLAY_NDX, FROM_CMD_LINE);
399        else if (streq(argv[i], "-geometry"))
400            ok = setOptString(&i, argc, argv, GEOMETRY_NDX, FROM_CMD_LINE);
401        else if (streq(argv[i], "-help") || streq(argv[i], "-?"))
402        {
403            usage(argc, argv);
404            exit(0);
405        }
406        else if (strpfx(argv[i], "-I"))
407            ok = addToList(&szInclPath, &numInclPath, &inclPath, &argv[i][2]);
408        else if (streq(argv[i], "-keycodes"))
409            ok = setOptString(&i, argc, argv, KEYCODES_NDX, FROM_CMD_LINE);
410        else if (streq(argv[i], "-keymap"))
411            ok = setOptString(&i, argc, argv, KEYMAP_NDX, FROM_CMD_LINE);
412        else if (streq(argv[i], "-layout"))
413            ok = setOptString(&i, argc, argv, LAYOUT_NDX, FROM_CMD_LINE);
414        else if (streq(argv[i], "-model"))
415            ok = setOptString(&i, argc, argv, MODEL_NDX, FROM_CMD_LINE);
416        else if (streq(argv[i], "-option"))
417        {
418            if ((i == argc - 1) || (argv[i + 1][0] == '\0')
419                || (argv[i + 1][0] == '-'))
420            {
421                clearOptions = True;
422                ok = addToList(&szOptions, &numOptions, &options, "");
423                if (i < argc - 1 && argv[i + 1][0] == '\0')
424                    i++;
425            }
426            else
427            {
428                ok = addToList(&szOptions, &numOptions, &options, argv[++i]);
429            }
430        }
431        else if (streq(argv[i], "-print"))
432            print = True;
433        else if (streq(argv[i], "-rules"))
434            ok = setOptString(&i, argc, argv, RULES_NDX, FROM_CMD_LINE);
435        else if (streq(argv[i], "-symbols"))
436            ok = setOptString(&i, argc, argv, SYMBOLS_NDX, FROM_CMD_LINE);
437        else if (streq(argv[i], "-synch"))
438            synch = True;
439        else if (streq(argv[i], "-types"))
440            ok = setOptString(&i, argc, argv, TYPES_NDX, FROM_CMD_LINE);
441        else if (streq(argv[i], "-verbose") || (streq(argv[i], "-v")))
442        {
443            if ((i < argc - 1) && (isdigit(argv[i + 1][0])))
444                verbose = atoi(argv[++i]);
445            else
446                verbose++;
447            if (verbose < 0)
448            {
449                ERR1("Illegal verbose level %d.  Reset to 0\n", verbose);
450                verbose = 0;
451            }
452            else if (verbose > 10)
453            {
454                ERR1("Illegal verbose level %d.  Reset to 10\n", verbose);
455                verbose = 10;
456            }
457            VMSG1(7, "Setting verbose level to %d\n", verbose);
458        }
459        else if (streq(argv[i], "-variant"))
460            ok = setOptString(&i, argc, argv, VARIANT_NDX, FROM_CMD_LINE);
461        else
462        {
463            ERR1("Error!   Option \"%s\" not recognized\n", argv[i]);
464            ok = False;
465        }
466    }
467
468    present = 0;
469    if (svValue[TYPES_NDX])
470        present++;
471    if (svValue[COMPAT_NDX])
472        present++;
473    if (svValue[SYMBOLS_NDX])
474        present++;
475    if (svValue[KEYCODES_NDX])
476        present++;
477    if (svValue[GEOMETRY_NDX])
478        present++;
479    if (svValue[CONFIG_NDX])
480        present++;
481    if (svValue[MODEL_NDX])
482        present++;
483    if (svValue[LAYOUT_NDX])
484        present++;
485    if (svValue[VARIANT_NDX])
486        present++;
487    if (svValue[KEYMAP_NDX] && present)
488    {
489        ERR("No other components can be specified when a keymap is present\n");
490        return False;
491    }
492    return ok;
493}
494
495/**
496 * Open a connection to the display and print error if it fails.
497 *
498 * @return True on success or False otherwise.
499 */
500Bool
501getDisplay(int argc, char **argv)
502{
503    int major, minor, why;
504
505    major = XkbMajorVersion;
506    minor = XkbMinorVersion;
507    dpy =
508        XkbOpenDisplay(svValue[DISPLAY_NDX], NULL, NULL, &major, &minor,
509                       &why);
510    if (!dpy)
511    {
512        if (svValue[DISPLAY_NDX] == NULL)
513            svValue[DISPLAY_NDX] = getenv("DISPLAY");
514        if (svValue[DISPLAY_NDX] == NULL)
515            svValue[DISPLAY_NDX] = "default display";
516        switch (why)
517        {
518        case XkbOD_BadLibraryVersion:
519            ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
520                 XkbMajorVersion, XkbMinorVersion);
521            ERR2("Xlib supports incompatible version %d.%02d\n",
522                 major, minor);
523            break;
524        case XkbOD_ConnectionRefused:
525            ERR1("Cannot open display \"%s\"\n", svValue[DISPLAY_NDX]);
526            break;
527        case XkbOD_NonXkbServer:
528            ERR1("XKB extension not present on %s\n", svValue[DISPLAY_NDX]);
529            break;
530        case XkbOD_BadServerVersion:
531            ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
532                 XkbMajorVersion, XkbMinorVersion);
533            ERR3("Server %s uses incompatible version %d.%02d\n",
534                 svValue[DISPLAY_NDX], major, minor);
535            break;
536        default:
537            ERR1("Unknown error %d from XkbOpenDisplay\n", why);
538            break;
539        }
540        return False;
541    }
542    if (synch)
543        XSynchronize(dpy, True);
544    return True;
545}
546
547/***====================================================================***/
548
549/**
550 * Retrieve xkb values from th the XKB_RULES_NAMES property and store their
551 * contents in svValues.
552 * If the property cannot be read, the built-in defaults are used.
553 *
554 * @return True.
555 */
556Bool
557getServerValues(void)
558{
559    XkbRF_VarDefsRec vd;
560    char *tmp = NULL;
561
562    if (!XkbRF_GetNamesProp(dpy, &tmp, &vd) || !tmp)
563    {
564        VMSG1(3, "Couldn't interpret %s property\n", _XKB_RF_NAMES_PROP_ATOM);
565        tmp = DFLT_XKB_RULES_FILE;
566        vd.model = DFLT_XKB_MODEL;
567        vd.layout = DFLT_XKB_LAYOUT;
568        vd.variant = NULL;
569        vd.options = NULL;
570        VMSG3(3, "Use defaults: rules - '%s' model - '%s' layout - '%s'\n",
571              tmp, vd.model, vd.layout);
572    }
573    if (tmp)
574        trySetString(RULES_NDX, tmp, FROM_SERVER);
575    if (vd.model)
576        trySetString(MODEL_NDX, vd.model, FROM_SERVER);
577    if (vd.layout)
578        trySetString(LAYOUT_NDX, vd.layout, FROM_SERVER);
579    if (vd.variant)
580        trySetString(VARIANT_NDX, vd.variant, FROM_SERVER);
581    if ((vd.options) && (!clearOptions))
582    {
583        addStringToOptions(vd.options, &szOptions, &numOptions, &options);
584        XFree(vd.options);
585    }
586    return True;
587}
588
589/***====================================================================***/
590
591FILE *
592findFileInPath(char *name, char *subdir)
593{
594    register int i;
595    char buf[PATH_MAX];
596    FILE *fp;
597
598    if (name[0] == '/')
599    {
600        fp = fopen(name, "r");
601        if ((verbose > 7) || ((!fp) && (verbose > 0)))
602            MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), name);
603        return fp;
604    }
605    for (i = 0; (i < numInclPath); i++)
606    {
607        if ((strlen(inclPath[i]) + strlen(subdir) + strlen(name) + 2) >
608            PATH_MAX)
609        {
610            VMSG3(0, "Path too long (%s/%s%s). Ignored.\n", inclPath[i],
611                  subdir, name);
612            continue;
613        }
614        sprintf(buf, "%s/%s%s", inclPath[i], subdir, name);
615        fp = fopen(name, "r");
616        if ((verbose > 7) || ((!fp) && (verbose > 5)))
617            MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), buf);
618        if (fp != NULL)
619            return fp;
620    }
621    return NULL;
622}
623
624/***====================================================================***/
625
626Bool
627addStringToOptions(char *opt_str, int *sz_opts, int *num_opts, char ***opts)
628{
629    char *tmp, *str, *next;
630    Bool ok = True;
631
632    if ((str = strdup(opt_str)) == NULL)
633        return False;
634    for (tmp = str, next = NULL; (tmp && *tmp != '\0') && ok; tmp = next)
635    {
636        next = strchr(str, ',');
637        if (next)
638        {
639            *next = '\0';
640            next++;
641        }
642        ok = addToList(sz_opts, num_opts, opts, tmp) && ok;
643    }
644    free(str);
645    return ok;
646}
647
648/***====================================================================***/
649
650char *
651stringFromOptions(char *orig, int numNew, char **newOpts)
652{
653    int len, i, nOut;
654
655    if (orig)
656        len = strlen(orig) + 1;
657    else
658        len = 0;
659    for (i = 0; i < numNew; i++)
660    {
661        if (newOpts[i])
662            len += strlen(newOpts[i]) + 1;
663    }
664    if (len < 1)
665        return NULL;
666    if (orig)
667    {
668        orig = (char *) realloc(orig, len);
669        if (!orig)
670        {
671            ERR("OOM in stringFromOptions\n");
672            return NULL;
673        }
674        nOut = 1;
675    }
676    else
677    {
678        orig = (char *) calloc(len, 1);
679        if (!orig)
680        {
681            ERR("OOM in stringFromOptions\n");
682            return NULL;
683        }
684        nOut = 0;
685    }
686    for (i = 0; i < numNew; i++)
687    {
688        if (!newOpts[i])
689            continue;
690        if (nOut > 0)
691        {
692            strcat(orig, ",");
693            strcat(orig, newOpts[i]);
694        }
695        else
696            strcpy(orig, newOpts[i]);
697        nOut++;
698    }
699    return orig;
700}
701
702/***====================================================================***/
703
704Bool
705applyConfig(char *name)
706{
707    FILE *fp;
708    Bool ok;
709
710    if ((fp = findFileInPath(name, "")) == NULL)
711        return False;
712    ok = XkbCFParse(fp, XkbCFDflts, NULL, &cfgResult);
713    fclose(fp);
714    if (!ok)
715    {
716        ERR1("Couldn't find configuration file \"%s\"\n", name);
717        return False;
718    }
719    if (cfgResult.rules_file)
720    {
721        trySetString(RULES_NDX, cfgResult.rules_file, FROM_CONFIG);
722        cfgResult.rules_file = NULL;
723    }
724    if (cfgResult.model)
725    {
726        trySetString(MODEL_NDX, cfgResult.model, FROM_CONFIG);
727        cfgResult.model = NULL;
728    }
729    if (cfgResult.layout)
730    {
731        trySetString(LAYOUT_NDX, cfgResult.layout, FROM_CONFIG);
732        cfgResult.layout = NULL;
733    }
734    if (cfgResult.variant)
735    {
736        trySetString(VARIANT_NDX, cfgResult.variant, FROM_CONFIG);
737        cfgResult.variant = NULL;
738    }
739    if (cfgResult.options)
740    {
741        addStringToOptions(cfgResult.options, &szOptions, &numOptions,
742                           &options);
743        cfgResult.options = NULL;
744    }
745    if (cfgResult.keymap)
746    {
747        trySetString(KEYMAP_NDX, cfgResult.keymap, FROM_CONFIG);
748        cfgResult.keymap = NULL;
749    }
750    if (cfgResult.keycodes)
751    {
752        trySetString(KEYCODES_NDX, cfgResult.keycodes, FROM_CONFIG);
753        cfgResult.keycodes = NULL;
754    }
755    if (cfgResult.geometry)
756    {
757        trySetString(GEOMETRY_NDX, cfgResult.geometry, FROM_CONFIG);
758        cfgResult.geometry = NULL;
759    }
760    if (cfgResult.symbols)
761    {
762        trySetString(SYMBOLS_NDX, cfgResult.symbols, FROM_CONFIG);
763        cfgResult.symbols = NULL;
764    }
765    if (cfgResult.types)
766    {
767        trySetString(TYPES_NDX, cfgResult.types, FROM_CONFIG);
768        cfgResult.types = NULL;
769    }
770    if (cfgResult.compat)
771    {
772        trySetString(COMPAT_NDX, cfgResult.compat, FROM_CONFIG);
773        cfgResult.compat = NULL;
774    }
775    if (verbose > 5)
776    {
777        MSG("After config file:\n");
778        dumpNames(True, True);
779    }
780    return True;
781}
782
783/**
784 * If any of model, layout, variant or options is specified, then compile the
785 * options into the
786 *
787 * @return True on success or false otherwise.
788 */
789Bool
790applyRules(void)
791{
792    int i;
793    char *rfName;
794
795    if (svSrc[MODEL_NDX] || svSrc[LAYOUT_NDX] || svSrc[VARIANT_NDX]
796        || options)
797    {
798        char buf[PATH_MAX];
799        XkbComponentNamesRec rnames;
800
801        if (svSrc[VARIANT_NDX] < svSrc[LAYOUT_NDX])
802            svValue[VARIANT_NDX] = NULL;
803
804        rdefs.model = svValue[MODEL_NDX];
805        rdefs.layout = svValue[LAYOUT_NDX];
806        rdefs.variant = svValue[VARIANT_NDX];
807        if (options)
808            rdefs.options =
809                stringFromOptions(rdefs.options, numOptions, options);
810
811        if (svSrc[RULES_NDX])
812            rfName = svValue[RULES_NDX];
813        else
814            rfName = DFLT_XKB_RULES_FILE;
815
816        if (rfName[0] == '/')
817        {
818            rules = XkbRF_Load(rfName, svValue[LOCALE_NDX], True, True);
819        }
820        else
821        {
822            /* try to load rules files from all include paths until the first
823             * we succeed with */
824            for (i = 0; (i < numInclPath) && (!rules); i++)
825            {
826                if ((strlen(inclPath[i]) + strlen(rfName) + 8) > PATH_MAX)
827                {
828                    VMSG2(0, "Path too long (%s/rules/%s). Ignored.\n",
829                          inclPath[i], rfName);
830                    continue;
831                }
832                sprintf(buf, "%s/rules/%s", inclPath[i], svValue[RULES_NDX]);
833                rules = XkbRF_Load(buf, svValue[LOCALE_NDX], True, True);
834            }
835        }
836        if (!rules)
837        {
838            ERR1("Couldn't find rules file (%s) \n", svValue[RULES_NDX]);
839            return False;
840        }
841        /* Let the rules file to the magic, then update the svValues with
842         * those returned after processing the rules */
843        XkbRF_GetComponents(rules, &rdefs, &rnames);
844        if (rnames.keycodes)
845        {
846            trySetString(KEYCODES_NDX, rnames.keycodes, FROM_RULES);
847            rnames.keycodes = NULL;
848        }
849        if (rnames.symbols)
850        {
851            trySetString(SYMBOLS_NDX, rnames.symbols, FROM_RULES);
852            rnames.symbols = NULL;
853        }
854        if (rnames.types)
855        {
856            trySetString(TYPES_NDX, rnames.types, FROM_RULES);
857            rnames.types = NULL;
858        }
859        if (rnames.compat)
860        {
861            trySetString(COMPAT_NDX, rnames.compat, FROM_RULES);
862            rnames.compat = NULL;
863        }
864        if (rnames.geometry)
865        {
866            trySetString(GEOMETRY_NDX, rnames.geometry, FROM_RULES);
867            rnames.geometry = NULL;
868        }
869        if (rnames.keymap)
870        {
871            trySetString(KEYMAP_NDX, rnames.keymap, FROM_RULES);
872            rnames.keymap = NULL;
873        }
874        if (verbose > 6)
875        {
876            MSG1("Applied rules from %s:\n", svValue[RULES_NDX]);
877            dumpNames(True, False);
878        }
879    }
880    else if (verbose > 6)
881    {
882        MSG("No rules variables specified.  Rules file ignored\n");
883    }
884    return True;
885}
886
887/* Primitive sanity check - filter out 'map names' (inside parenthesis) */
888/* that can confuse xkbcomp parser */
889static Bool
890checkName(char *name, char *string)
891{
892    char *i = name, *opar = NULL;
893    Bool ret = True;
894
895    if (!name)
896        return True;
897
898    while (*i)
899    {
900        if (opar == NULL)
901        {
902            if (*i == '(')
903                opar = i;
904        }
905        else
906        {
907            if ((*i == '(') || (*i == '|') || (*i == '+'))
908            {
909                ret = False;
910                break;
911            }
912            if (*i == ')')
913                opar = NULL;
914        }
915        i++;
916    }
917    if (opar)
918        ret = False;
919    if (!ret)
920    {
921        char c;
922        int n = 1;
923        for (i = opar + 1; *i && n; i++)
924        {
925            if (*i == '(')
926                n++;
927            if (*i == ')')
928                n--;
929        }
930        if (*i)
931            i++;
932        c = *i;
933        *i = '\0';
934        ERR1("Illegal map name '%s' ", opar);
935        *i = c;
936        ERR2("in %s name '%s'\n", string, name);
937    }
938    return ret;
939}
940
941void
942printKeymap(void)
943{
944    MSG("xkb_keymap {\n");
945    if (svValue[KEYCODES_NDX])
946        MSG1("\txkb_keycodes  { include \"%s\"\t};\n", svValue[KEYCODES_NDX]);
947    if (svValue[TYPES_NDX])
948        MSG1("\txkb_types     { include \"%s\"\t};\n", svValue[TYPES_NDX]);
949    if (svValue[COMPAT_NDX])
950        MSG1("\txkb_compat    { include \"%s\"\t};\n", svValue[COMPAT_NDX]);
951    if (svValue[SYMBOLS_NDX])
952        MSG1("\txkb_symbols   { include \"%s\"\t};\n", svValue[SYMBOLS_NDX]);
953    if (svValue[GEOMETRY_NDX])
954        MSG1("\txkb_geometry  { include \"%s\"\t};\n", svValue[GEOMETRY_NDX]);
955    MSG("};\n");
956}
957
958Bool
959applyComponentNames(void)
960{
961    if (!checkName(svValue[TYPES_NDX], "types"))
962        return False;
963    if (!checkName(svValue[COMPAT_NDX], "compat"))
964        return False;
965    if (!checkName(svValue[SYMBOLS_NDX], "symbols"))
966        return False;
967    if (!checkName(svValue[KEYCODES_NDX], "keycodes"))
968        return False;
969    if (!checkName(svValue[GEOMETRY_NDX], "geometry"))
970        return False;
971    if (!checkName(svValue[KEYMAP_NDX], "keymap"))
972        return False;
973
974    if (verbose > 5)
975    {
976        MSG("Trying to build keymap using the following components:\n");
977        dumpNames(False, True);
978    }
979    /* Upload the new description to the server. */
980    if (dpy && !print)
981    {
982        XkbComponentNamesRec cmdNames;
983        cmdNames.types = svValue[TYPES_NDX];
984        cmdNames.compat = svValue[COMPAT_NDX];
985        cmdNames.symbols = svValue[SYMBOLS_NDX];
986        cmdNames.keycodes = svValue[KEYCODES_NDX];
987        cmdNames.geometry = svValue[GEOMETRY_NDX];
988        cmdNames.keymap = svValue[KEYMAP_NDX];
989        xkb = XkbGetKeyboardByName(dpy, deviceSpec, &cmdNames,
990                                   XkbGBN_AllComponentsMask,
991                                   XkbGBN_AllComponentsMask &
992                                   (~XkbGBN_GeometryMask), True);
993        if (!xkb)
994        {
995            ERR("Error loading new keyboard description\n");
996            return False;
997        }
998        /* update the XKB root property */
999        if (svValue[RULES_NDX] && (rdefs.model || rdefs.layout))
1000        {
1001            if (!XkbRF_SetNamesProp(dpy, svValue[RULES_NDX], &rdefs))
1002            {
1003                VMSG(0, "Error updating the XKB names property\n");
1004            }
1005        }
1006    }
1007    if (print)
1008    {
1009        printKeymap();
1010    }
1011    return True;
1012}
1013
1014
1015int
1016main(int argc, char **argv)
1017{
1018    if ((!parseArgs(argc, argv)) || (!getDisplay(argc, argv)))
1019        exit(-1);
1020    svValue[LOCALE_NDX] = setlocale(LC_ALL, svValue[LOCALE_NDX]);
1021    svSrc[LOCALE_NDX] = FROM_SERVER;
1022    VMSG1(7, "locale is %s\n", svValue[LOCALE_NDX]);
1023    if (dpy)
1024        getServerValues();
1025    if (svValue[CONFIG_NDX] && (!applyConfig(svValue[CONFIG_NDX])))
1026        exit(-3);
1027    if (!applyRules())
1028        exit(-4);
1029    if (!applyComponentNames())
1030        exit(-5);
1031    if (dpy)
1032        XCloseDisplay(dpy);
1033    exit(0);
1034}
1035