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