setxkbmap.c revision 13e6bc1c
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    {
746        ERR1("Couldn't find configuration file \"%s\"\n", name);
747        return False;
748    }
749    ok = XkbCFParse(fp, XkbCFDflts, NULL, &cfgResult);
750    fclose(fp);
751    if (!ok)
752    {
753        ERR1("Couldn't parse configuration file \"%s\"\n", name);
754        return False;
755    }
756    if (cfgResult.rules_file)
757    {
758        trySetString(&settings.rules, cfgResult.rules_file, FROM_CONFIG);
759        cfgResult.rules_file = NULL;
760    }
761    if (cfgResult.model)
762    {
763        trySetString(&settings.model, cfgResult.model, FROM_CONFIG);
764        cfgResult.model = NULL;
765    }
766    if (cfgResult.layout)
767    {
768        trySetString(&settings.layout, cfgResult.layout, FROM_CONFIG);
769        cfgResult.layout = NULL;
770    }
771    if (cfgResult.variant)
772    {
773        trySetString(&settings.variant, cfgResult.variant, FROM_CONFIG);
774        cfgResult.variant = NULL;
775    }
776    if (cfgResult.options)
777    {
778        addStringToOptions(cfgResult.options, &options);
779        cfgResult.options = NULL;
780    }
781    if (cfgResult.keymap)
782    {
783        trySetString(&settings.keymap, cfgResult.keymap, FROM_CONFIG);
784        cfgResult.keymap = NULL;
785    }
786    if (cfgResult.keycodes)
787    {
788        trySetString(&settings.keycodes, cfgResult.keycodes, FROM_CONFIG);
789        cfgResult.keycodes = NULL;
790    }
791    if (cfgResult.geometry)
792    {
793        trySetString(&settings.geometry, cfgResult.geometry, FROM_CONFIG);
794        cfgResult.geometry = NULL;
795    }
796    if (cfgResult.symbols)
797    {
798        trySetString(&settings.symbols, cfgResult.symbols, FROM_CONFIG);
799        cfgResult.symbols = NULL;
800    }
801    if (cfgResult.types)
802    {
803        trySetString(&settings.types, cfgResult.types, FROM_CONFIG);
804        cfgResult.types = NULL;
805    }
806    if (cfgResult.compat)
807    {
808        trySetString(&settings.compat, cfgResult.compat, FROM_CONFIG);
809        cfgResult.compat = NULL;
810    }
811    if (verbose > 5)
812    {
813        MSG("After config file:\n");
814        dumpNames(True, True);
815    }
816    return True;
817}
818
819XkbRF_RulesPtr
820tryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules)
821{
822    XkbRF_RulesPtr rules = NULL;
823    VMSG1(7, "Trying to load rules file %s...\n", name);
824    rules = XkbRF_Load(name, locale, wantDesc, wantRules);
825    if (rules)
826    {
827        VMSG(7, "Success.\n");
828    }
829    return rules;
830}
831
832/**
833 * If any of model, layout, variant or options is specified, then compile the
834 * options into the
835 *
836 * @return True on success or false otherwise.
837 */
838Bool
839applyRules(void)
840{
841    int i;
842    char *rfName;
843    XkbRF_RulesPtr rules = NULL;
844
845    if (settings.model.src || settings.layout.src || settings.variant.src
846        || options.item)
847    {
848        char buf[PATH_MAX];
849        XkbComponentNamesRec rnames;
850
851        if (settings.variant.src < settings.layout.src)
852            settings.variant.value = NULL;
853
854        rdefs.model = settings.model.value;
855        rdefs.layout = settings.layout.value;
856        rdefs.variant = settings.variant.value;
857        if (options.item)
858            rdefs.options =
859                stringFromOptions(rdefs.options, &options);
860
861        if (settings.rules.src)
862            rfName = settings.rules.value;
863        else
864            rfName = DFLT_XKB_RULES_FILE;
865
866        if (rfName[0] == '/')
867        {
868            rules = tryLoadRules(rfName, settings.locale.value, True, True);
869        }
870        else
871        {
872            /* try to load rules files from all include paths until the first
873             * we succeed with */
874            for (i = 0; (i < inclPath.num) && (!rules); i++)
875            {
876                if (snprintf(buf, PATH_MAX, "%s/rules/%s",
877                             inclPath.item[i], rfName) >= PATH_MAX)
878                {
879                    VMSG2(0, "Path too long (%s/rules/%s). Ignored.\n",
880                          inclPath.item[i], rfName);
881                    continue;
882                }
883                rules = tryLoadRules(buf, settings.locale.value, True, True);
884            }
885        }
886        if (!rules)
887        {
888            ERR1("Couldn't find rules file (%s) \n", rfName);
889            return False;
890        }
891        /* Let the rules file to the magic, then update the svValues with
892         * those returned after processing the rules */
893        XkbRF_GetComponents(rules, &rdefs, &rnames);
894        if (rnames.keycodes)
895        {
896            trySetString(&settings.keycodes, rnames.keycodes, FROM_RULES);
897            rnames.keycodes = NULL;
898        }
899        if (rnames.symbols)
900        {
901            trySetString(&settings.symbols, rnames.symbols, FROM_RULES);
902            rnames.symbols = NULL;
903        }
904        if (rnames.types)
905        {
906            trySetString(&settings.types, rnames.types, FROM_RULES);
907            rnames.types = NULL;
908        }
909        if (rnames.compat)
910        {
911            trySetString(&settings.compat, rnames.compat, FROM_RULES);
912            rnames.compat = NULL;
913        }
914        if (rnames.geometry)
915        {
916            trySetString(&settings.geometry, rnames.geometry, FROM_RULES);
917            rnames.geometry = NULL;
918        }
919        if (rnames.keymap)
920        {
921            trySetString(&settings.keymap, rnames.keymap, FROM_RULES);
922            rnames.keymap = NULL;
923        }
924        if (verbose > 6)
925        {
926            MSG1("Applied rules from %s:\n", rfName);
927            dumpNames(True, False);
928        }
929    }
930    else if (verbose > 6)
931    {
932        MSG("No rules variables specified.  Rules file ignored\n");
933    }
934    return True;
935}
936
937/* Primitive sanity check - filter out 'map names' (inside parenthesis) */
938/* that can confuse xkbcomp parser */
939static Bool
940checkName(char *name, const char *string)
941{
942    char *i = name, *opar = NULL;
943    Bool ret = True;
944
945    if (!name)
946        return True;
947
948    while (*i)
949    {
950        if (opar == NULL)
951        {
952            if (*i == '(')
953                opar = i;
954        }
955        else
956        {
957            if ((*i == '(') || (*i == '|') || (*i == '+'))
958            {
959                ret = False;
960                break;
961            }
962            if (*i == ')')
963                opar = NULL;
964        }
965        i++;
966    }
967    if (opar)
968        ret = False;
969    if (!ret)
970    {
971        char c;
972        int n = 1;
973        for (i = opar + 1; *i && n; i++)
974        {
975            if (*i == '(')
976                n++;
977            if (*i == ')')
978                n--;
979        }
980        if (*i)
981            i++;
982        c = *i;
983        *i = '\0';
984        ERR1("Illegal map name '%s' ", opar);
985        *i = c;
986        ERR2("in %s name '%s'\n", string, name);
987    }
988    return ret;
989}
990
991void
992printKeymap(void)
993{
994    MSG("xkb_keymap {\n");
995    if (settings.keycodes.value)
996        MSG1("\txkb_keycodes  { include \"%s\"\t};\n", settings.keycodes.value);
997    if (settings.types.value)
998        MSG1("\txkb_types     { include \"%s\"\t};\n", settings.types.value);
999    if (settings.compat.value)
1000        MSG1("\txkb_compat    { include \"%s\"\t};\n", settings.compat.value);
1001    if (settings.symbols.value)
1002        MSG1("\txkb_symbols   { include \"%s\"\t};\n", settings.symbols.value);
1003    if (settings.geometry.value)
1004        MSG1("\txkb_geometry  { include \"%s\"\t};\n", settings.geometry.value);
1005    MSG("};\n");
1006}
1007
1008Bool
1009applyComponentNames(void)
1010{
1011    if (!checkName(settings.types.value, "types"))
1012        return False;
1013    if (!checkName(settings.compat.value, "compat"))
1014        return False;
1015    if (!checkName(settings.symbols.value, "symbols"))
1016        return False;
1017    if (!checkName(settings.keycodes.value, "keycodes"))
1018        return False;
1019    if (!checkName(settings.geometry.value, "geometry"))
1020        return False;
1021    if (!checkName(settings.keymap.value, "keymap"))
1022        return False;
1023
1024    if (verbose > 5)
1025    {
1026        MSG("Trying to build keymap using the following components:\n");
1027        dumpNames(False, True);
1028    }
1029    /* Upload the new description to the server. */
1030    if (dpy && !print && !query)
1031    {
1032        XkbComponentNamesRec cmdNames = {
1033            .keymap = settings.keymap.value,
1034            .keycodes = settings.keycodes.value,
1035            .types = settings.types.value,
1036            .compat = settings.compat.value,
1037            .symbols = settings.symbols.value,
1038            .geometry = settings.geometry.value
1039        };
1040
1041        xkb = XkbGetKeyboardByName(dpy, deviceSpec, &cmdNames,
1042                                   XkbGBN_AllComponentsMask,
1043                                   XkbGBN_AllComponentsMask &
1044                                   (~XkbGBN_GeometryMask), True);
1045        if (!xkb)
1046        {
1047            ERR("Error loading new keyboard description\n");
1048            return False;
1049        }
1050        /* update the XKB root property */
1051        if (settings.rules.value && (rdefs.model || rdefs.layout))
1052        {
1053            if (!XkbRF_SetNamesProp(dpy, settings.rules.value, &rdefs))
1054            {
1055                VMSG(0, "Error updating the XKB names property\n");
1056            }
1057        }
1058    }
1059    if (print)
1060    {
1061        printKeymap();
1062    }
1063    if (query)
1064    {
1065        dumpNames(True, False);
1066    }
1067    return True;
1068}
1069
1070
1071int
1072main(int argc, char **argv)
1073{
1074    if ((!parseArgs(argc, argv)) || (!getDisplay(argc, argv)))
1075        exit(-1);
1076    settings.locale.value = setlocale(LC_ALL, settings.locale.value);
1077    settings.locale.src = FROM_SERVER;
1078    VMSG1(7, "locale is %s\n", settings.locale.value);
1079    if (dpy)
1080        getServerValues();
1081    if (settings.config.value && (!applyConfig(settings.config.value)))
1082        exit(-3);
1083    if (!applyRules())
1084        exit(-4);
1085    if (!applyComponentNames())
1086        exit(-5);
1087    if (dpy)
1088        XCloseDisplay(dpy);
1089    exit(0);
1090}
1091