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