setxkbmap.c revision bda5b58f
17d5e3a19Smrg/************************************************************
27d5e3a19Smrg Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
37d5e3a19Smrg
47d5e3a19Smrg Permission to use, copy, modify, and distribute this
57d5e3a19Smrg software and its documentation for any purpose and without
67d5e3a19Smrg fee is hereby granted, provided that the above copyright
77d5e3a19Smrg notice appear in all copies and that both that copyright
87d5e3a19Smrg notice and this permission notice appear in supporting
9bda5b58fSmrg documentation, and that the name of Silicon Graphics not be
10bda5b58fSmrg used in advertising or publicity pertaining to distribution
117d5e3a19Smrg of the software without specific prior written permission.
12bda5b58fSmrg Silicon Graphics makes no representation about the suitability
137d5e3a19Smrg of this software for any purpose. It is provided "as is"
147d5e3a19Smrg without any express or implied warranty.
15bda5b58fSmrg
16bda5b58fSmrg SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17bda5b58fSmrg SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
187d5e3a19Smrg AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19bda5b58fSmrg GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20bda5b58fSmrg DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21bda5b58fSmrg DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
227d5e3a19Smrg OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
237d5e3a19Smrg THE USE OR PERFORMANCE OF THIS SOFTWARE.
247d5e3a19Smrg
257d5e3a19Smrg ********************************************************/
267d5e3a19Smrg
277d5e3a19Smrg#include <stdio.h>
287d5e3a19Smrg#include <stdlib.h>
297d5e3a19Smrg#include <locale.h>
307d5e3a19Smrg#include <limits.h>
317d5e3a19Smrg#include <ctype.h>
327d5e3a19Smrg#include <X11/Xlib.h>
337d5e3a19Smrg#include <X11/Xos.h>
347d5e3a19Smrg#include <X11/XKBlib.h>
357d5e3a19Smrg#include <X11/extensions/XKBfile.h>
367d5e3a19Smrg#include <X11/extensions/XKBconfig.h>
377d5e3a19Smrg#include <X11/extensions/XKBrules.h>
387d5e3a19Smrg
397d5e3a19Smrg#ifndef PATH_MAX
407d5e3a19Smrg#ifdef MAXPATHLEN
417d5e3a19Smrg#define PATH_MAX MAXPATHLEN
427d5e3a19Smrg#else
437d5e3a19Smrg#define PATH_MAX 1024
447d5e3a19Smrg#endif
457d5e3a19Smrg#endif
467d5e3a19Smrg
477d5e3a19Smrg#ifndef DFLT_XKB_CONFIG_ROOT
48bda5b58fSmrg#define DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb"
497d5e3a19Smrg#endif
507d5e3a19Smrg#ifndef DFLT_XKB_RULES_FILE
51bda5b58fSmrg#define DFLT_XKB_RULES_FILE "base"
527d5e3a19Smrg#endif
537d5e3a19Smrg#ifndef DFLT_XKB_LAYOUT
54bda5b58fSmrg#define DFLT_XKB_LAYOUT "us"
557d5e3a19Smrg#endif
567d5e3a19Smrg#ifndef DFLT_XKB_MODEL
57bda5b58fSmrg#define DFLT_XKB_MODEL "pc105"
587d5e3a19Smrg#endif
597d5e3a19Smrg
60bda5b58fSmrg/* Constants to state how a value was obtained. The order of these
611568b75bSmrg * is important, the bigger the higher the priority.
621568b75bSmrg * e.g. FROM_CONFIG overrides FROM_SERVER */
63bda5b58fSmrgenum source {
64bda5b58fSmrg    UNDEFINED = 0,
65bda5b58fSmrg    FROM_SERVER,          /* Retrieved from server at runtime. */
66bda5b58fSmrg    FROM_RULES,           /* Xkb rules file. */
67bda5b58fSmrg    FROM_CONFIG,          /* Command-line specified config file. */
68bda5b58fSmrg    FROM_CMD_LINE,        /* Specified at the cmdline. */
69bda5b58fSmrg    NUM_SOURCES
70bda5b58fSmrg};
717d5e3a19Smrg
727d5e3a19Smrg/***====================================================================***/
731568b75bSmrgstatic Bool print = False;
74765486e8Smrgstatic Bool query = False;
751568b75bSmrgstatic Bool synch = False;
761568b75bSmrgstatic int verbose = 5;
771568b75bSmrg
781568b75bSmrgstatic Display *dpy;
791568b75bSmrg
801568b75bSmrg/**
811568b75bSmrg * human-readable versions of FROM_CONFIG, FROM_SERVER, etc. Used for error
821568b75bSmrg * reporting.
831568b75bSmrg */
84bda5b58fSmrgstatic const char *srcName[NUM_SOURCES] = {
851568b75bSmrg    "undefined", "X server", "rules file", "config file", "command line"
867d5e3a19Smrg};
877d5e3a19Smrg
88bda5b58fSmrgstruct setting {
89bda5b58fSmrg    char const  *name;  /* Human-readable setting name. Used for error reporting. */
90bda5b58fSmrg    char        *value; /* Holds the value. */
91bda5b58fSmrg    enum source  src;   /* Holds the source. */
92bda5b58fSmrg};
93bda5b58fSmrg
94bda5b58fSmrgtypedef struct setting setting_t;
95bda5b58fSmrg
96bda5b58fSmrgstruct settings {
97bda5b58fSmrg    setting_t rules;     /* Rules file */
98bda5b58fSmrg    setting_t config;    /* Config file (if used) */
99bda5b58fSmrg    setting_t display;   /* X display name */
100bda5b58fSmrg    setting_t locale;    /* Machine's locale */
101bda5b58fSmrg    setting_t model;
102bda5b58fSmrg    setting_t layout;
103bda5b58fSmrg    setting_t variant;
104bda5b58fSmrg    setting_t keycodes;
105bda5b58fSmrg    setting_t types;
106bda5b58fSmrg    setting_t compat;
107bda5b58fSmrg    setting_t symbols;
108bda5b58fSmrg    setting_t geometry;
109bda5b58fSmrg    setting_t keymap;
110bda5b58fSmrg};
111bda5b58fSmrg
112bda5b58fSmrgtypedef struct settings settings_t;
113bda5b58fSmrg
114bda5b58fSmrgstatic settings_t settings = {
115bda5b58fSmrg    { "rules file",         NULL, UNDEFINED },
116bda5b58fSmrg    { "config file",        NULL, UNDEFINED },
117bda5b58fSmrg    { "X display",          NULL, UNDEFINED },
118bda5b58fSmrg    { "locale",             NULL, UNDEFINED },
119bda5b58fSmrg    { "keyboard model",     NULL, UNDEFINED },
120bda5b58fSmrg    { "keyboard layout",    NULL, UNDEFINED },
121bda5b58fSmrg    { "layout variant",     NULL, UNDEFINED },
122bda5b58fSmrg    { "keycodes",           NULL, UNDEFINED },
123bda5b58fSmrg    { "types",              NULL, UNDEFINED },
124bda5b58fSmrg    { "compatibility map",  NULL, UNDEFINED },
125bda5b58fSmrg    { "symbols",            NULL, UNDEFINED },
126bda5b58fSmrg    { "geometry",           NULL, UNDEFINED },
127bda5b58fSmrg    { "keymap",             NULL, UNDEFINED }
1287d5e3a19Smrg};
1297d5e3a19Smrg
1301568b75bSmrgstatic XkbConfigRtrnRec cfgResult;
1317d5e3a19Smrg
1321568b75bSmrgstatic XkbRF_RulesPtr rules = NULL;
1331568b75bSmrgstatic XkbRF_VarDefsRec rdefs;
1347d5e3a19Smrg
1351568b75bSmrgstatic Bool clearOptions = False;
1367d5e3a19Smrg
137bda5b58fSmrgstruct list {
138bda5b58fSmrg    char  **item;   /* Array of items. */
139bda5b58fSmrg    int     sz;     /* Size of array. */
140bda5b58fSmrg    int     num;    /* Number of used elements. */
141bda5b58fSmrg};
142bda5b58fSmrg
143bda5b58fSmrgtypedef struct list list_t;
144bda5b58fSmrg
145bda5b58fSmrgstatic list_t options = { NULL, 0, 0 };
146bda5b58fSmrg
147bda5b58fSmrgstatic list_t inclPath = { NULL, 0, 0 };
1487d5e3a19Smrg
1491568b75bSmrgstatic XkbDescPtr xkb = NULL;
1507d5e3a19Smrg
1511568b75bSmrgstatic int deviceSpec = XkbUseCoreKbd;
1527d5e3a19Smrg
1537d5e3a19Smrg/***====================================================================***/
1547d5e3a19Smrg
155bda5b58fSmrg#define streq(s1,s2)    (strcmp(s1,s2)==0)
156bda5b58fSmrg#define strpfx(s1,s2)   (strncmp(s1,s2,strlen(s2))==0)
157bda5b58fSmrg
158bda5b58fSmrg#define MSG(s)          printf(s)
159bda5b58fSmrg#define MSG1(s,a)       printf(s,a)
160bda5b58fSmrg#define MSG2(s,a,b)     printf(s,a,b)
161bda5b58fSmrg#define MSG3(s,a,b,c)   printf(s,a,b,c)
1627d5e3a19Smrg
163bda5b58fSmrg#define VMSG(l,s)        if (verbose>(l)) printf(s)
164bda5b58fSmrg#define VMSG1(l,s,a)     if (verbose>(l)) printf(s,a)
165bda5b58fSmrg#define VMSG2(l,s,a,b)   if (verbose>(l)) printf(s,a,b)
166bda5b58fSmrg#define VMSG3(l,s,a,b,c) if (verbose>(l)) printf(s,a,b,c)
1677d5e3a19Smrg
168bda5b58fSmrg#define ERR(s)          fprintf(stderr,s)
169bda5b58fSmrg#define ERR1(s,a)       fprintf(stderr,s,a)
170bda5b58fSmrg#define ERR2(s,a,b)     fprintf(stderr,s,a,b)
171bda5b58fSmrg#define ERR3(s,a,b,c)   fprintf(stderr,s,a,b,c)
1727d5e3a19Smrg
173bda5b58fSmrg#define OOM(ptr)        do { if ((ptr) == NULL) { ERR("Out of memory.\n"); exit(-1); } } while (0)
1747d5e3a19Smrg
1757d5e3a19Smrg/***====================================================================***/
1767d5e3a19Smrg
177bda5b58fSmrgBool addToList(list_t *list, const char *newVal);
1781568b75bSmrgvoid usage(int argc, char **argv);
1791568b75bSmrgvoid dumpNames(Bool wantRules, Bool wantCNames);
180bda5b58fSmrgvoid trySetString(setting_t * setting, char *newVal, enum source src);
181bda5b58fSmrgBool setOptString(int *arg, int argc, char **argv, setting_t *setting, enum source src);
1821568b75bSmrgint parseArgs(int argc, char **argv);
1831568b75bSmrgBool getDisplay(int argc, char **argv);
1841568b75bSmrgBool getServerValues(void);
185bda5b58fSmrgFILE *findFileInPath(char *name);
186bda5b58fSmrgBool addStringToOptions(char *opt_str, list_t *opts);
187bda5b58fSmrgchar *stringFromOptions(char *orig, list_t *newOpts);
1881568b75bSmrgBool applyConfig(char *name);
189bda5b58fSmrgXkbRF_RulesPtr tryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules);
1901568b75bSmrgBool applyRules(void);
1911568b75bSmrgBool applyComponentNames(void);
1921568b75bSmrgvoid printKeymap(void);
1937d5e3a19Smrg
1947d5e3a19Smrg/***====================================================================***/
1957d5e3a19Smrg
196bda5b58fSmrg/*
197bda5b58fSmrg    If newVal is NULL or empty string, the list is cleared.
198bda5b58fSmrg    Otherwise newVal is added to the end of the list (if it is not present in the list yet).
199bda5b58fSmrg*/
200bda5b58fSmrg
2017d5e3a19SmrgBool
202bda5b58fSmrgaddToList(list_t *list, const char *newVal)
2037d5e3a19Smrg{
2041568b75bSmrg    register int i;
2051568b75bSmrg
2061568b75bSmrg    if ((!newVal) || (!newVal[0]))
2071568b75bSmrg    {
208bda5b58fSmrg        list->num = 0;
2091568b75bSmrg        return True;
2101568b75bSmrg    }
211bda5b58fSmrg    for (i = 0; i < list->num; i++)
2121568b75bSmrg    {
213bda5b58fSmrg        if (streq(list->item[i], newVal))
2141568b75bSmrg            return True;
2151568b75bSmrg    }
216bda5b58fSmrg    if ((list->item == NULL) || (list->sz < 1))
2171568b75bSmrg    {
218bda5b58fSmrg        list->num = 0;
219bda5b58fSmrg        list->sz = 4;
220bda5b58fSmrg        list->item = (char **) calloc(list->sz, sizeof(char *));
221bda5b58fSmrg        OOM(list->item);
2221568b75bSmrg    }
223bda5b58fSmrg    else if (list->num >= list->sz)
2241568b75bSmrg    {
225bda5b58fSmrg        list->sz *= 2;
226bda5b58fSmrg        list->item = (char **) realloc(list->item, (list->sz) * sizeof(char *));
227bda5b58fSmrg        OOM(list->item);
2281568b75bSmrg    }
229bda5b58fSmrg    list->item[list->num] = strdup(newVal);
230bda5b58fSmrg    OOM(list->item[list->num]);
231bda5b58fSmrg    list->num += 1;
2327d5e3a19Smrg    return True;
2337d5e3a19Smrg}
2347d5e3a19Smrg
2357d5e3a19Smrg/***====================================================================***/
2367d5e3a19Smrg
2377d5e3a19Smrgvoid
2381568b75bSmrgusage(int argc, char **argv)
2397d5e3a19Smrg{
240bda5b58fSmrg    MSG1(
241bda5b58fSmrg        "Usage: %s [options] [<layout> [<variant> [<option> ... ]]]\n"
242bda5b58fSmrg        "Options:\n"
243bda5b58fSmrg        "  -?, -help           Print this message\n"
244bda5b58fSmrg        "  -compat <name>      Specifies compatibility map component name\n"
245bda5b58fSmrg        "  -config <file>      Specifies configuration file to use\n"
246bda5b58fSmrg        "  -device <deviceid>  Specifies the device ID to use\n"
247bda5b58fSmrg        "  -display <dpy>      Specifies display to use\n"
248bda5b58fSmrg        "  -geometry <name>    Specifies geometry component name\n"
249bda5b58fSmrg        "  -I<dir>             Add <dir> to list of directories to be used\n"
250bda5b58fSmrg        "  -keycodes <name>    Specifies keycodes component name\n"
251bda5b58fSmrg        "  -keymap <name>      Specifies name of keymap to load\n"
252bda5b58fSmrg        "  -layout <name>      Specifies layout used to choose component names\n"
253bda5b58fSmrg        "  -model <name>       Specifies model used to choose component names\n"
254bda5b58fSmrg        "  -option <name>      Adds an option used to choose component names\n"
255bda5b58fSmrg        "  -print              Print a complete xkb_keymap description and exit\n"
256bda5b58fSmrg        "  -query              Print the current layout settings and exit\n"
257bda5b58fSmrg        "  -rules <name>       Name of rules file to use\n"
258bda5b58fSmrg        "  -symbols <name>     Specifies symbols component name\n"
259bda5b58fSmrg        "  -synch              Synchronize request with X server\n"
260bda5b58fSmrg        "  -types <name>       Specifies types component name\n"
261bda5b58fSmrg        "  -v[erbose] [<lvl>]  Sets verbosity (1..10).  Higher values yield\n"
262bda5b58fSmrg        "                      more messages\n"
263bda5b58fSmrg        "  -variant <name>     Specifies layout variant used to choose component names\n",
264bda5b58fSmrg        argv[0]
265bda5b58fSmrg    );
2667d5e3a19Smrg}
2677d5e3a19Smrg
2687d5e3a19Smrgvoid
2691568b75bSmrgdumpNames(Bool wantRules, Bool wantCNames)
2707d5e3a19Smrg{
2711568b75bSmrg    if (wantRules)
2721568b75bSmrg    {
273bda5b58fSmrg        if (settings.rules.value)
274bda5b58fSmrg            MSG1("rules:      %s\n", settings.rules.value);
275bda5b58fSmrg        if (settings.model.value)
276bda5b58fSmrg            MSG1("model:      %s\n", settings.model.value);
277bda5b58fSmrg        if (settings.layout.value)
278bda5b58fSmrg            MSG1("layout:     %s\n", settings.layout.value);
279bda5b58fSmrg        if (settings.variant.value)
280bda5b58fSmrg            MSG1("variant:    %s\n", settings.variant.value);
281bda5b58fSmrg        if (options.item)
2821568b75bSmrg        {
283bda5b58fSmrg            char *opt_str = stringFromOptions(NULL, &options);
2841568b75bSmrg            MSG1("options:    %s\n", opt_str);
2851568b75bSmrg            free(opt_str);
2861568b75bSmrg        }
2871568b75bSmrg    }
2881568b75bSmrg    if (wantCNames)
2891568b75bSmrg    {
290bda5b58fSmrg        if (settings.keymap.value)
291bda5b58fSmrg            MSG1("keymap:     %s\n", settings.keymap.value);
292bda5b58fSmrg        if (settings.keycodes.value)
293bda5b58fSmrg            MSG1("keycodes:   %s\n", settings.keycodes.value);
294bda5b58fSmrg        if (settings.types.value)
295bda5b58fSmrg            MSG1("types:      %s\n", settings.types.value);
296bda5b58fSmrg        if (settings.compat.value)
297bda5b58fSmrg            MSG1("compat:     %s\n", settings.compat.value);
298bda5b58fSmrg        if (settings.symbols.value)
299bda5b58fSmrg            MSG1("symbols:    %s\n", settings.symbols.value);
300bda5b58fSmrg        if (settings.geometry.value)
301bda5b58fSmrg            MSG1("geometry:   %s\n", settings.geometry.value);
3027d5e3a19Smrg    }
3037d5e3a19Smrg    return;
3047d5e3a19Smrg}
3057d5e3a19Smrg
3067d5e3a19Smrg/***====================================================================***/
3077d5e3a19Smrg
3081568b75bSmrg/**
3091568b75bSmrg * Set the given string (obtained from src) in the svValue/svSrc globals.
3101568b75bSmrg * If the given item is already set, it is overridden if the original source
3111568b75bSmrg * is less significant than the given one.
3121568b75bSmrg *
3131568b75bSmrg * @param which What value is it (one of RULES_NDX, CONFIG_NDX, ...)
3141568b75bSmrg */
3157d5e3a19Smrgvoid
316bda5b58fSmrgtrySetString(setting_t *setting, char *newVal, enum source src)
3177d5e3a19Smrg{
318bda5b58fSmrg    if (setting->value != NULL)
3191568b75bSmrg    {
320bda5b58fSmrg        if (setting->src == src)
3211568b75bSmrg        {
3221568b75bSmrg            VMSG2(0, "Warning! More than one %s from %s\n",
323bda5b58fSmrg                  setting->name, srcName[src]);
3241568b75bSmrg            VMSG2(0, "         Using \"%s\", ignoring \"%s\"\n",
325bda5b58fSmrg                  setting->value, newVal);
3261568b75bSmrg            return;
3271568b75bSmrg        }
328bda5b58fSmrg        else if (setting->src > src)
3291568b75bSmrg        {
330bda5b58fSmrg            VMSG1(5, "Warning! Multiple definitions of %s\n", setting->name);
3311568b75bSmrg            VMSG2(5, "         Using %s, ignoring %s\n",
332bda5b58fSmrg                  srcName[setting->src], srcName[src]);
3331568b75bSmrg            return;
3341568b75bSmrg        }
3351568b75bSmrg    }
336bda5b58fSmrg    setting->src = src;
337bda5b58fSmrg    setting->value = newVal;
3387d5e3a19Smrg    return;
3397d5e3a19Smrg}
3407d5e3a19Smrg
3417d5e3a19SmrgBool
342bda5b58fSmrgsetOptString(int *arg, int argc, char **argv, setting_t *setting, enum source src)
3437d5e3a19Smrg{
3441568b75bSmrg    int ndx;
3451568b75bSmrg    char *opt;
3461568b75bSmrg
3471568b75bSmrg    ndx = *arg;
3481568b75bSmrg    opt = argv[ndx];
3491568b75bSmrg    if (ndx >= argc - 1)
3501568b75bSmrg    {
351bda5b58fSmrg        VMSG1(0, "No %s specified on the command line\n", setting->name);
3521568b75bSmrg        VMSG1(0, "Trailing %s option ignored\n", opt);
3531568b75bSmrg        return True;
3547d5e3a19Smrg    }
3557d5e3a19Smrg    ndx++;
3561568b75bSmrg    *arg = ndx;
357bda5b58fSmrg    if (setting->value != NULL)
3581568b75bSmrg    {
359bda5b58fSmrg        if (setting->src == src)
3601568b75bSmrg        {
361bda5b58fSmrg            VMSG2(0, "More than one %s on %s\n", setting->name, srcName[src]);
362bda5b58fSmrg            VMSG2(0, "Using \"%s\", ignoring \"%s\"\n", setting->value,
3631568b75bSmrg                  argv[ndx]);
3641568b75bSmrg            return True;
3651568b75bSmrg        }
366bda5b58fSmrg        else if (setting->src > src)
3671568b75bSmrg        {
368bda5b58fSmrg            VMSG1(5, "Multiple definitions of %s\n", setting->name);
369bda5b58fSmrg            VMSG2(5, "Using %s, ignoring %s\n", srcName[setting->src],
3701568b75bSmrg                  srcName[src]);
3711568b75bSmrg            return True;
3721568b75bSmrg        }
3731568b75bSmrg    }
374bda5b58fSmrg    setting->src = src;
375bda5b58fSmrg    setting->value = argv[ndx];
3767d5e3a19Smrg    return True;
3777d5e3a19Smrg}
3787d5e3a19Smrg
3797d5e3a19Smrg/***====================================================================***/
3807d5e3a19Smrg
3811568b75bSmrg/**
3821568b75bSmrg * Parse commandline arguments.
3831568b75bSmrg * Return True on success or False if an unrecognized option has been
3841568b75bSmrg * specified.
3851568b75bSmrg */
3867d5e3a19Smrgint
3871568b75bSmrgparseArgs(int argc, char **argv)
3887d5e3a19Smrg{
3891568b75bSmrg    int i;
3901568b75bSmrg    Bool ok;
3911568b75bSmrg    unsigned present;
3921568b75bSmrg
3931568b75bSmrg    ok = True;
394bda5b58fSmrg    addToList(&inclPath, ".");
395bda5b58fSmrg    addToList(&inclPath, DFLT_XKB_CONFIG_ROOT);
3961568b75bSmrg    for (i = 1; (i < argc) && ok; i++)
3971568b75bSmrg    {
3981568b75bSmrg        if (argv[i][0] != '-')
3991568b75bSmrg        {
4001568b75bSmrg            /* Allow a call like "setxkbmap us" to work. Layout is default,
4011568b75bSmrg               if -layout is given, then try parsing variant, then options */
402bda5b58fSmrg            if (!settings.layout.src)
403bda5b58fSmrg                trySetString(&settings.layout, argv[i], FROM_CMD_LINE);
404bda5b58fSmrg            else if (!settings.variant.src)
405bda5b58fSmrg                trySetString(&settings.variant, argv[i], FROM_CMD_LINE);
4061568b75bSmrg            else
407bda5b58fSmrg                ok = addToList(&options, argv[i]);
4081568b75bSmrg        }
4091568b75bSmrg        else if (streq(argv[i], "-compat"))
410bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.compat, FROM_CMD_LINE);
4111568b75bSmrg        else if (streq(argv[i], "-config"))
412bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.config, FROM_CMD_LINE);
4131568b75bSmrg        else if (streq(argv[i], "-device"))
414bda5b58fSmrg        {
415bda5b58fSmrg            if ( ++i < argc ) {
416bda5b58fSmrg                deviceSpec = atoi(argv[i]); /* only allow device IDs, not names */
417bda5b58fSmrg            } else {
418bda5b58fSmrg                usage(argc, argv);
419bda5b58fSmrg                exit(-1);
420bda5b58fSmrg            }
421bda5b58fSmrg        }
4221568b75bSmrg        else if (streq(argv[i], "-display"))
423bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.display, FROM_CMD_LINE);
4241568b75bSmrg        else if (streq(argv[i], "-geometry"))
425bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.geometry, FROM_CMD_LINE);
4261568b75bSmrg        else if (streq(argv[i], "-help") || streq(argv[i], "-?"))
4271568b75bSmrg        {
4281568b75bSmrg            usage(argc, argv);
4291568b75bSmrg            exit(0);
4301568b75bSmrg        }
431bda5b58fSmrg        else if (streq(argv[i], "-I")) /* space between -I and path */
432bda5b58fSmrg        {
433bda5b58fSmrg            if ( ++i < argc )
434bda5b58fSmrg                ok = addToList(&inclPath, argv[i]);
435bda5b58fSmrg            else
436bda5b58fSmrg                VMSG(0, "No directory specified on the command line\n"
437bda5b58fSmrg                     "Trailing -I option ignored\n");
438bda5b58fSmrg        }
439bda5b58fSmrg        else if (strpfx(argv[i], "-I")) /* no space between -I and path */
440bda5b58fSmrg            ok = addToList(&inclPath, &argv[i][2]);
4411568b75bSmrg        else if (streq(argv[i], "-keycodes"))
442bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.keycodes, FROM_CMD_LINE);
4431568b75bSmrg        else if (streq(argv[i], "-keymap"))
444bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.keymap, FROM_CMD_LINE);
4451568b75bSmrg        else if (streq(argv[i], "-layout"))
446bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.layout, FROM_CMD_LINE);
4471568b75bSmrg        else if (streq(argv[i], "-model"))
448bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.model, FROM_CMD_LINE);
4491568b75bSmrg        else if (streq(argv[i], "-option"))
4501568b75bSmrg        {
4511568b75bSmrg            if ((i == argc - 1) || (argv[i + 1][0] == '\0')
4521568b75bSmrg                || (argv[i + 1][0] == '-'))
4531568b75bSmrg            {
4541568b75bSmrg                clearOptions = True;
455bda5b58fSmrg                ok = addToList(&options, "");
4561568b75bSmrg                if (i < argc - 1 && argv[i + 1][0] == '\0')
4571568b75bSmrg                    i++;
4581568b75bSmrg            }
4591568b75bSmrg            else
4601568b75bSmrg            {
461bda5b58fSmrg                ok = addToList(&options, argv[++i]);
4621568b75bSmrg            }
4631568b75bSmrg        }
4641568b75bSmrg        else if (streq(argv[i], "-print"))
4651568b75bSmrg            print = True;
466765486e8Smrg        else if (streq(argv[i], "-query"))
467765486e8Smrg            query = True;
4681568b75bSmrg        else if (streq(argv[i], "-rules"))
469bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.rules, FROM_CMD_LINE);
4701568b75bSmrg        else if (streq(argv[i], "-symbols"))
471bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.symbols, FROM_CMD_LINE);
4721568b75bSmrg        else if (streq(argv[i], "-synch"))
4731568b75bSmrg            synch = True;
4741568b75bSmrg        else if (streq(argv[i], "-types"))
475bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.types, FROM_CMD_LINE);
4761568b75bSmrg        else if (streq(argv[i], "-verbose") || (streq(argv[i], "-v")))
4771568b75bSmrg        {
4781568b75bSmrg            if ((i < argc - 1) && (isdigit(argv[i + 1][0])))
4791568b75bSmrg                verbose = atoi(argv[++i]);
4801568b75bSmrg            else
4811568b75bSmrg                verbose++;
4821568b75bSmrg            if (verbose < 0)
4831568b75bSmrg            {
4841568b75bSmrg                ERR1("Illegal verbose level %d.  Reset to 0\n", verbose);
4851568b75bSmrg                verbose = 0;
4861568b75bSmrg            }
4871568b75bSmrg            else if (verbose > 10)
4881568b75bSmrg            {
4891568b75bSmrg                ERR1("Illegal verbose level %d.  Reset to 10\n", verbose);
4901568b75bSmrg                verbose = 10;
4911568b75bSmrg            }
4921568b75bSmrg            VMSG1(7, "Setting verbose level to %d\n", verbose);
4931568b75bSmrg        }
4941568b75bSmrg        else if (streq(argv[i], "-variant"))
495bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.variant, FROM_CMD_LINE);
4961568b75bSmrg        else
4971568b75bSmrg        {
4981568b75bSmrg            ERR1("Error!   Option \"%s\" not recognized\n", argv[i]);
4991568b75bSmrg            ok = False;
5001568b75bSmrg        }
5011568b75bSmrg    }
5021568b75bSmrg
5031568b75bSmrg    present = 0;
504bda5b58fSmrg    if (settings.types.value)
5051568b75bSmrg        present++;
506bda5b58fSmrg    if (settings.compat.value)
5071568b75bSmrg        present++;
508bda5b58fSmrg    if (settings.symbols.value)
5091568b75bSmrg        present++;
510bda5b58fSmrg    if (settings.keycodes.value)
5111568b75bSmrg        present++;
512bda5b58fSmrg    if (settings.geometry.value)
5131568b75bSmrg        present++;
514bda5b58fSmrg    if (settings.config.value)
5151568b75bSmrg        present++;
516bda5b58fSmrg    if (settings.model.value)
5171568b75bSmrg        present++;
518bda5b58fSmrg    if (settings.layout.value)
5191568b75bSmrg        present++;
520bda5b58fSmrg    if (settings.variant.value)
5211568b75bSmrg        present++;
522bda5b58fSmrg    if (settings.keymap.value && present)
5231568b75bSmrg    {
5241568b75bSmrg        ERR("No other components can be specified when a keymap is present\n");
5251568b75bSmrg        return False;
5267d5e3a19Smrg    }
5277d5e3a19Smrg    return ok;
5287d5e3a19Smrg}
5297d5e3a19Smrg
5301568b75bSmrg/**
5311568b75bSmrg * Open a connection to the display and print error if it fails.
5321568b75bSmrg *
5331568b75bSmrg * @return True on success or False otherwise.
5341568b75bSmrg */
5357d5e3a19SmrgBool
5361568b75bSmrggetDisplay(int argc, char **argv)
5377d5e3a19Smrg{
5381568b75bSmrg    int major, minor, why;
5391568b75bSmrg
5401568b75bSmrg    major = XkbMajorVersion;
5411568b75bSmrg    minor = XkbMinorVersion;
5421568b75bSmrg    dpy =
543bda5b58fSmrg        XkbOpenDisplay(settings.display.value, NULL, NULL, &major, &minor,
5441568b75bSmrg                       &why);
5451568b75bSmrg    if (!dpy)
5461568b75bSmrg    {
547bda5b58fSmrg        if (settings.display.value == NULL)
548bda5b58fSmrg            settings.display.value = getenv("DISPLAY");
549bda5b58fSmrg        if (settings.display.value == NULL)
550bda5b58fSmrg            settings.display.value = "default display";
5511568b75bSmrg        switch (why)
5521568b75bSmrg        {
5531568b75bSmrg        case XkbOD_BadLibraryVersion:
5541568b75bSmrg            ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
5551568b75bSmrg                 XkbMajorVersion, XkbMinorVersion);
5561568b75bSmrg            ERR2("Xlib supports incompatible version %d.%02d\n",
5571568b75bSmrg                 major, minor);
5581568b75bSmrg            break;
5591568b75bSmrg        case XkbOD_ConnectionRefused:
560bda5b58fSmrg            ERR1("Cannot open display \"%s\"\n", settings.display.value);
5611568b75bSmrg            break;
5621568b75bSmrg        case XkbOD_NonXkbServer:
563bda5b58fSmrg            ERR1("XKB extension not present on %s\n", settings.display.value);
5641568b75bSmrg            break;
5651568b75bSmrg        case XkbOD_BadServerVersion:
5661568b75bSmrg            ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
5671568b75bSmrg                 XkbMajorVersion, XkbMinorVersion);
5681568b75bSmrg            ERR3("Server %s uses incompatible version %d.%02d\n",
569bda5b58fSmrg                 settings.display.value, major, minor);
5701568b75bSmrg            break;
5711568b75bSmrg        default:
5721568b75bSmrg            ERR1("Unknown error %d from XkbOpenDisplay\n", why);
5731568b75bSmrg            break;
5741568b75bSmrg        }
5751568b75bSmrg        return False;
5767d5e3a19Smrg    }
5777d5e3a19Smrg    if (synch)
5781568b75bSmrg        XSynchronize(dpy, True);
5797d5e3a19Smrg    return True;
5807d5e3a19Smrg}
5817d5e3a19Smrg
5827d5e3a19Smrg/***====================================================================***/
5837d5e3a19Smrg
5841568b75bSmrg/**
585765486e8Smrg * Retrieve xkb values from the XKB_RULES_NAMES property and store their
5861568b75bSmrg * contents in svValues.
5871568b75bSmrg * If the property cannot be read, the built-in defaults are used.
5881568b75bSmrg *
5891568b75bSmrg * @return True.
5901568b75bSmrg */
5917d5e3a19SmrgBool
5927d5e3a19SmrggetServerValues(void)
5937d5e3a19Smrg{
5941568b75bSmrg    XkbRF_VarDefsRec vd;
5951568b75bSmrg    char *tmp = NULL;
5967d5e3a19Smrg
5971568b75bSmrg    if (!XkbRF_GetNamesProp(dpy, &tmp, &vd) || !tmp)
5981568b75bSmrg    {
5991568b75bSmrg        VMSG1(3, "Couldn't interpret %s property\n", _XKB_RF_NAMES_PROP_ATOM);
6007d5e3a19Smrg        tmp = DFLT_XKB_RULES_FILE;
6017d5e3a19Smrg        vd.model = DFLT_XKB_MODEL;
6027d5e3a19Smrg        vd.layout = DFLT_XKB_LAYOUT;
6037d5e3a19Smrg        vd.variant = NULL;
6047d5e3a19Smrg        vd.options = NULL;
6051568b75bSmrg        VMSG3(3, "Use defaults: rules - '%s' model - '%s' layout - '%s'\n",
6061568b75bSmrg              tmp, vd.model, vd.layout);
6077d5e3a19Smrg    }
6087d5e3a19Smrg    if (tmp)
609bda5b58fSmrg        trySetString(&settings.rules, tmp, FROM_SERVER);
6107d5e3a19Smrg    if (vd.model)
611bda5b58fSmrg        trySetString(&settings.model, vd.model, FROM_SERVER);
6127d5e3a19Smrg    if (vd.layout)
613bda5b58fSmrg        trySetString(&settings.layout, vd.layout, FROM_SERVER);
6147d5e3a19Smrg    if (vd.variant)
615bda5b58fSmrg        trySetString(&settings.variant, vd.variant, FROM_SERVER);
6161568b75bSmrg    if ((vd.options) && (!clearOptions))
6171568b75bSmrg    {
618bda5b58fSmrg        addStringToOptions(vd.options, &options);
6191568b75bSmrg        XFree(vd.options);
6207d5e3a19Smrg    }
6217d5e3a19Smrg    return True;
6227d5e3a19Smrg}
6237d5e3a19Smrg
6247d5e3a19Smrg/***====================================================================***/
6257d5e3a19Smrg
6267d5e3a19SmrgFILE *
627bda5b58fSmrgfindFileInPath(char *name)
6287d5e3a19Smrg{
6291568b75bSmrg    register int i;
6301568b75bSmrg    char buf[PATH_MAX];
6311568b75bSmrg    FILE *fp;
6321568b75bSmrg
6331568b75bSmrg    if (name[0] == '/')
6341568b75bSmrg    {
6351568b75bSmrg        fp = fopen(name, "r");
6361568b75bSmrg        if ((verbose > 7) || ((!fp) && (verbose > 0)))
6371568b75bSmrg            MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), name);
6381568b75bSmrg        return fp;
6391568b75bSmrg    }
640bda5b58fSmrg    for (i = 0; (i < inclPath.num); i++)
6411568b75bSmrg    {
642bda5b58fSmrg        if (snprintf(buf, PATH_MAX, "%s/%s", inclPath.item[i], name) >=
6431568b75bSmrg            PATH_MAX)
6441568b75bSmrg        {
645bda5b58fSmrg            VMSG2(0, "Path too long (%s/%s). Ignored.\n", inclPath.item[i],
646bda5b58fSmrg                  name);
6471568b75bSmrg            continue;
6481568b75bSmrg        }
649bda5b58fSmrg        fp = fopen(buf, "r");
6501568b75bSmrg        if ((verbose > 7) || ((!fp) && (verbose > 5)))
6511568b75bSmrg            MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), buf);
6521568b75bSmrg        if (fp != NULL)
6531568b75bSmrg            return fp;
6547d5e3a19Smrg    }
6557d5e3a19Smrg    return NULL;
6567d5e3a19Smrg}
6577d5e3a19Smrg
6587d5e3a19Smrg/***====================================================================***/
6597d5e3a19Smrg
6607d5e3a19SmrgBool
661bda5b58fSmrgaddStringToOptions(char *opt_str, list_t *opts)
6627d5e3a19Smrg{
6631568b75bSmrg    char *tmp, *str, *next;
6641568b75bSmrg    Bool ok = True;
6657d5e3a19Smrg
666bda5b58fSmrg    str = strdup(opt_str);
667bda5b58fSmrg    OOM(str);
668bda5b58fSmrg    for (tmp = str; (tmp && *tmp != '\0') && ok; tmp = next)
6691568b75bSmrg    {
6701568b75bSmrg        next = strchr(str, ',');
6711568b75bSmrg        if (next)
6721568b75bSmrg        {
6731568b75bSmrg            *next = '\0';
6741568b75bSmrg            next++;
6751568b75bSmrg        }
676bda5b58fSmrg        ok = addToList(opts, tmp) && ok;
6777d5e3a19Smrg    }
6787d5e3a19Smrg    free(str);
6797d5e3a19Smrg    return ok;
6807d5e3a19Smrg}
6817d5e3a19Smrg
6827d5e3a19Smrg/***====================================================================***/
6837d5e3a19Smrg
6847d5e3a19Smrgchar *
685bda5b58fSmrgstringFromOptions(char *orig, list_t *newOpts)
6867d5e3a19Smrg{
6871568b75bSmrg    int len, i, nOut;
6881568b75bSmrg
6891568b75bSmrg    if (orig)
6901568b75bSmrg        len = strlen(orig) + 1;
6911568b75bSmrg    else
6921568b75bSmrg        len = 0;
693bda5b58fSmrg    for (i = 0; i < newOpts->num; i++)
6941568b75bSmrg    {
695bda5b58fSmrg        if (newOpts->item[i])
696bda5b58fSmrg            len += strlen(newOpts->item[i]) + 1;
6971568b75bSmrg    }
6981568b75bSmrg    if (len < 1)
6991568b75bSmrg        return NULL;
7001568b75bSmrg    if (orig)
7011568b75bSmrg    {
7021568b75bSmrg        orig = (char *) realloc(orig, len);
703bda5b58fSmrg        OOM(orig);
7041568b75bSmrg        nOut = 1;
7051568b75bSmrg    }
7061568b75bSmrg    else
7071568b75bSmrg    {
7081568b75bSmrg        orig = (char *) calloc(len, 1);
709bda5b58fSmrg        OOM(orig);
7101568b75bSmrg        nOut = 0;
7111568b75bSmrg    }
712bda5b58fSmrg    for (i = 0; i < newOpts->num; i++)
7131568b75bSmrg    {
714bda5b58fSmrg        if (!newOpts->item[i])
7151568b75bSmrg            continue;
7161568b75bSmrg        if (nOut > 0)
7171568b75bSmrg        {
7181568b75bSmrg            strcat(orig, ",");
719bda5b58fSmrg            strcat(orig, newOpts->item[i]);
7201568b75bSmrg        }
7211568b75bSmrg        else
722bda5b58fSmrg            strcpy(orig, newOpts->item[i]);
7231568b75bSmrg        nOut++;
7247d5e3a19Smrg    }
7257d5e3a19Smrg    return orig;
7267d5e3a19Smrg}
7277d5e3a19Smrg
7287d5e3a19Smrg/***====================================================================***/
7297d5e3a19Smrg
7307d5e3a19SmrgBool
7317d5e3a19SmrgapplyConfig(char *name)
7327d5e3a19Smrg{
7331568b75bSmrg    FILE *fp;
7341568b75bSmrg    Bool ok;
7351568b75bSmrg
736bda5b58fSmrg    if ((fp = findFileInPath(name)) == NULL)
7371568b75bSmrg        return False;
7381568b75bSmrg    ok = XkbCFParse(fp, XkbCFDflts, NULL, &cfgResult);
7397d5e3a19Smrg    fclose(fp);
7401568b75bSmrg    if (!ok)
7411568b75bSmrg    {
7421568b75bSmrg        ERR1("Couldn't find configuration file \"%s\"\n", name);
7431568b75bSmrg        return False;
7447d5e3a19Smrg    }
7451568b75bSmrg    if (cfgResult.rules_file)
7461568b75bSmrg    {
747bda5b58fSmrg        trySetString(&settings.rules, cfgResult.rules_file, FROM_CONFIG);
7481568b75bSmrg        cfgResult.rules_file = NULL;
7497d5e3a19Smrg    }
7501568b75bSmrg    if (cfgResult.model)
7511568b75bSmrg    {
752bda5b58fSmrg        trySetString(&settings.model, cfgResult.model, FROM_CONFIG);
7531568b75bSmrg        cfgResult.model = NULL;
7547d5e3a19Smrg    }
7551568b75bSmrg    if (cfgResult.layout)
7561568b75bSmrg    {
757bda5b58fSmrg        trySetString(&settings.layout, cfgResult.layout, FROM_CONFIG);
7581568b75bSmrg        cfgResult.layout = NULL;
7597d5e3a19Smrg    }
7601568b75bSmrg    if (cfgResult.variant)
7611568b75bSmrg    {
762bda5b58fSmrg        trySetString(&settings.variant, cfgResult.variant, FROM_CONFIG);
7631568b75bSmrg        cfgResult.variant = NULL;
7647d5e3a19Smrg    }
7651568b75bSmrg    if (cfgResult.options)
7661568b75bSmrg    {
767bda5b58fSmrg        addStringToOptions(cfgResult.options, &options);
7681568b75bSmrg        cfgResult.options = NULL;
7697d5e3a19Smrg    }
7701568b75bSmrg    if (cfgResult.keymap)
7711568b75bSmrg    {
772bda5b58fSmrg        trySetString(&settings.keymap, cfgResult.keymap, FROM_CONFIG);
7731568b75bSmrg        cfgResult.keymap = NULL;
7747d5e3a19Smrg    }
7751568b75bSmrg    if (cfgResult.keycodes)
7761568b75bSmrg    {
777bda5b58fSmrg        trySetString(&settings.keycodes, cfgResult.keycodes, FROM_CONFIG);
7781568b75bSmrg        cfgResult.keycodes = NULL;
7797d5e3a19Smrg    }
7801568b75bSmrg    if (cfgResult.geometry)
7811568b75bSmrg    {
782bda5b58fSmrg        trySetString(&settings.geometry, cfgResult.geometry, FROM_CONFIG);
7831568b75bSmrg        cfgResult.geometry = NULL;
7847d5e3a19Smrg    }
7851568b75bSmrg    if (cfgResult.symbols)
7861568b75bSmrg    {
787bda5b58fSmrg        trySetString(&settings.symbols, cfgResult.symbols, FROM_CONFIG);
7881568b75bSmrg        cfgResult.symbols = NULL;
7897d5e3a19Smrg    }
7901568b75bSmrg    if (cfgResult.types)
7911568b75bSmrg    {
792bda5b58fSmrg        trySetString(&settings.types, cfgResult.types, FROM_CONFIG);
7931568b75bSmrg        cfgResult.types = NULL;
7947d5e3a19Smrg    }
7951568b75bSmrg    if (cfgResult.compat)
7961568b75bSmrg    {
797bda5b58fSmrg        trySetString(&settings.compat, cfgResult.compat, FROM_CONFIG);
7981568b75bSmrg        cfgResult.compat = NULL;
7997d5e3a19Smrg    }
8001568b75bSmrg    if (verbose > 5)
8011568b75bSmrg    {
8021568b75bSmrg        MSG("After config file:\n");
8031568b75bSmrg        dumpNames(True, True);
8047d5e3a19Smrg    }
8057d5e3a19Smrg    return True;
8067d5e3a19Smrg}
8077d5e3a19Smrg
808bda5b58fSmrgXkbRF_RulesPtr
809bda5b58fSmrgtryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules)
810bda5b58fSmrg{
811bda5b58fSmrg    XkbRF_RulesPtr rules = NULL;
812bda5b58fSmrg    VMSG1(7, "Trying to load rules file %s...\n", name);
813bda5b58fSmrg    rules = XkbRF_Load(name, locale, wantDesc, wantRules);
814bda5b58fSmrg    if (rules)
815bda5b58fSmrg    {
816bda5b58fSmrg        VMSG(7, "Success.\n");
817bda5b58fSmrg    }
818bda5b58fSmrg    return rules;
819bda5b58fSmrg}
820bda5b58fSmrg
8211568b75bSmrg/**
8221568b75bSmrg * If any of model, layout, variant or options is specified, then compile the
8231568b75bSmrg * options into the
8241568b75bSmrg *
8251568b75bSmrg * @return True on success or false otherwise.
8261568b75bSmrg */
8277d5e3a19SmrgBool
8287d5e3a19SmrgapplyRules(void)
8297d5e3a19Smrg{
8301568b75bSmrg    int i;
8311568b75bSmrg    char *rfName;
8327d5e3a19Smrg
833bda5b58fSmrg    if (settings.model.src || settings.layout.src || settings.variant.src
834bda5b58fSmrg        || options.item)
8351568b75bSmrg    {
8361568b75bSmrg        char buf[PATH_MAX];
8371568b75bSmrg        XkbComponentNamesRec rnames;
8387d5e3a19Smrg
839bda5b58fSmrg        if (settings.variant.src < settings.layout.src)
840bda5b58fSmrg            settings.variant.value = NULL;
8417d5e3a19Smrg
842bda5b58fSmrg        rdefs.model = settings.model.value;
843bda5b58fSmrg        rdefs.layout = settings.layout.value;
844bda5b58fSmrg        rdefs.variant = settings.variant.value;
845bda5b58fSmrg        if (options.item)
8461568b75bSmrg            rdefs.options =
847bda5b58fSmrg                stringFromOptions(rdefs.options, &options);
8481568b75bSmrg
849bda5b58fSmrg        if (settings.rules.src)
850bda5b58fSmrg            rfName = settings.rules.value;
8511568b75bSmrg        else
8521568b75bSmrg            rfName = DFLT_XKB_RULES_FILE;
8531568b75bSmrg
8541568b75bSmrg        if (rfName[0] == '/')
8551568b75bSmrg        {
856bda5b58fSmrg            rules = tryLoadRules(rfName, settings.locale.value, True, True);
8571568b75bSmrg        }
8581568b75bSmrg        else
8591568b75bSmrg        {
8601568b75bSmrg            /* try to load rules files from all include paths until the first
8611568b75bSmrg             * we succeed with */
862bda5b58fSmrg            for (i = 0; (i < inclPath.num) && (!rules); i++)
8631568b75bSmrg            {
864bda5b58fSmrg                if (snprintf(buf, PATH_MAX, "%s/rules/%s",
865bda5b58fSmrg                             inclPath.item[i], rfName) >= PATH_MAX)
8661568b75bSmrg                {
8671568b75bSmrg                    VMSG2(0, "Path too long (%s/rules/%s). Ignored.\n",
868bda5b58fSmrg                          inclPath.item[i], rfName);
8691568b75bSmrg                    continue;
8701568b75bSmrg                }
871bda5b58fSmrg                rules = tryLoadRules(buf, settings.locale.value, True, True);
8721568b75bSmrg            }
8731568b75bSmrg        }
8741568b75bSmrg        if (!rules)
8751568b75bSmrg        {
876bda5b58fSmrg            ERR1("Couldn't find rules file (%s) \n", rfName);
8771568b75bSmrg            return False;
8781568b75bSmrg        }
8791568b75bSmrg        /* Let the rules file to the magic, then update the svValues with
8801568b75bSmrg         * those returned after processing the rules */
8811568b75bSmrg        XkbRF_GetComponents(rules, &rdefs, &rnames);
8821568b75bSmrg        if (rnames.keycodes)
8831568b75bSmrg        {
884bda5b58fSmrg            trySetString(&settings.keycodes, rnames.keycodes, FROM_RULES);
8851568b75bSmrg            rnames.keycodes = NULL;
8861568b75bSmrg        }
8871568b75bSmrg        if (rnames.symbols)
8881568b75bSmrg        {
889bda5b58fSmrg            trySetString(&settings.symbols, rnames.symbols, FROM_RULES);
8901568b75bSmrg            rnames.symbols = NULL;
8911568b75bSmrg        }
8921568b75bSmrg        if (rnames.types)
8931568b75bSmrg        {
894bda5b58fSmrg            trySetString(&settings.types, rnames.types, FROM_RULES);
8951568b75bSmrg            rnames.types = NULL;
8961568b75bSmrg        }
8971568b75bSmrg        if (rnames.compat)
8981568b75bSmrg        {
899bda5b58fSmrg            trySetString(&settings.compat, rnames.compat, FROM_RULES);
9001568b75bSmrg            rnames.compat = NULL;
9011568b75bSmrg        }
9021568b75bSmrg        if (rnames.geometry)
9031568b75bSmrg        {
904bda5b58fSmrg            trySetString(&settings.geometry, rnames.geometry, FROM_RULES);
9051568b75bSmrg            rnames.geometry = NULL;
9061568b75bSmrg        }
9071568b75bSmrg        if (rnames.keymap)
9081568b75bSmrg        {
909bda5b58fSmrg            trySetString(&settings.keymap, rnames.keymap, FROM_RULES);
9101568b75bSmrg            rnames.keymap = NULL;
9111568b75bSmrg        }
9121568b75bSmrg        if (verbose > 6)
9131568b75bSmrg        {
914bda5b58fSmrg            MSG1("Applied rules from %s:\n", rfName);
9151568b75bSmrg            dumpNames(True, False);
9161568b75bSmrg        }
9171568b75bSmrg    }
9181568b75bSmrg    else if (verbose > 6)
9191568b75bSmrg    {
9201568b75bSmrg        MSG("No rules variables specified.  Rules file ignored\n");
9217d5e3a19Smrg    }
9227d5e3a19Smrg    return True;
9237d5e3a19Smrg}
9247d5e3a19Smrg
9257d5e3a19Smrg/* Primitive sanity check - filter out 'map names' (inside parenthesis) */
9267d5e3a19Smrg/* that can confuse xkbcomp parser */
9271568b75bSmrgstatic Bool
928bda5b58fSmrgcheckName(char *name, const char *string)
9297d5e3a19Smrg{
9301568b75bSmrg    char *i = name, *opar = NULL;
9311568b75bSmrg    Bool ret = True;
9321568b75bSmrg
9331568b75bSmrg    if (!name)
9341568b75bSmrg        return True;
9351568b75bSmrg
9361568b75bSmrg    while (*i)
9371568b75bSmrg    {
9381568b75bSmrg        if (opar == NULL)
9391568b75bSmrg        {
9401568b75bSmrg            if (*i == '(')
9411568b75bSmrg                opar = i;
9421568b75bSmrg        }
9431568b75bSmrg        else
9441568b75bSmrg        {
9451568b75bSmrg            if ((*i == '(') || (*i == '|') || (*i == '+'))
9461568b75bSmrg            {
9471568b75bSmrg                ret = False;
9481568b75bSmrg                break;
9491568b75bSmrg            }
9501568b75bSmrg            if (*i == ')')
9511568b75bSmrg                opar = NULL;
9521568b75bSmrg        }
9531568b75bSmrg        i++;
9541568b75bSmrg    }
9551568b75bSmrg    if (opar)
9561568b75bSmrg        ret = False;
9571568b75bSmrg    if (!ret)
9581568b75bSmrg    {
9591568b75bSmrg        char c;
9601568b75bSmrg        int n = 1;
9611568b75bSmrg        for (i = opar + 1; *i && n; i++)
9621568b75bSmrg        {
9631568b75bSmrg            if (*i == '(')
9641568b75bSmrg                n++;
9651568b75bSmrg            if (*i == ')')
9661568b75bSmrg                n--;
9671568b75bSmrg        }
9681568b75bSmrg        if (*i)
9691568b75bSmrg            i++;
9701568b75bSmrg        c = *i;
9711568b75bSmrg        *i = '\0';
9721568b75bSmrg        ERR1("Illegal map name '%s' ", opar);
9731568b75bSmrg        *i = c;
9741568b75bSmrg        ERR2("in %s name '%s'\n", string, name);
9751568b75bSmrg    }
9761568b75bSmrg    return ret;
9777d5e3a19Smrg}
9787d5e3a19Smrg
9797d5e3a19Smrgvoid
9807d5e3a19SmrgprintKeymap(void)
9817d5e3a19Smrg{
9827d5e3a19Smrg    MSG("xkb_keymap {\n");
983bda5b58fSmrg    if (settings.keycodes.value)
984bda5b58fSmrg        MSG1("\txkb_keycodes  { include \"%s\"\t};\n", settings.keycodes.value);
985bda5b58fSmrg    if (settings.types.value)
986bda5b58fSmrg        MSG1("\txkb_types     { include \"%s\"\t};\n", settings.types.value);
987bda5b58fSmrg    if (settings.compat.value)
988bda5b58fSmrg        MSG1("\txkb_compat    { include \"%s\"\t};\n", settings.compat.value);
989bda5b58fSmrg    if (settings.symbols.value)
990bda5b58fSmrg        MSG1("\txkb_symbols   { include \"%s\"\t};\n", settings.symbols.value);
991bda5b58fSmrg    if (settings.geometry.value)
992bda5b58fSmrg        MSG1("\txkb_geometry  { include \"%s\"\t};\n", settings.geometry.value);
9937d5e3a19Smrg    MSG("};\n");
9947d5e3a19Smrg}
9957d5e3a19Smrg
9967d5e3a19SmrgBool
9977d5e3a19SmrgapplyComponentNames(void)
9987d5e3a19Smrg{
999bda5b58fSmrg    if (!checkName(settings.types.value, "types"))
10001568b75bSmrg        return False;
1001bda5b58fSmrg    if (!checkName(settings.compat.value, "compat"))
10021568b75bSmrg        return False;
1003bda5b58fSmrg    if (!checkName(settings.symbols.value, "symbols"))
10041568b75bSmrg        return False;
1005bda5b58fSmrg    if (!checkName(settings.keycodes.value, "keycodes"))
10061568b75bSmrg        return False;
1007bda5b58fSmrg    if (!checkName(settings.geometry.value, "geometry"))
10081568b75bSmrg        return False;
1009bda5b58fSmrg    if (!checkName(settings.keymap.value, "keymap"))
10101568b75bSmrg        return False;
10111568b75bSmrg
10121568b75bSmrg    if (verbose > 5)
10131568b75bSmrg    {
10141568b75bSmrg        MSG("Trying to build keymap using the following components:\n");
10151568b75bSmrg        dumpNames(False, True);
10161568b75bSmrg    }
10171568b75bSmrg    /* Upload the new description to the server. */
1018765486e8Smrg    if (dpy && !print && !query)
10191568b75bSmrg    {
10201568b75bSmrg        XkbComponentNamesRec cmdNames;
1021bda5b58fSmrg        cmdNames.types = settings.types.value;
1022bda5b58fSmrg        cmdNames.compat = settings.compat.value;
1023bda5b58fSmrg        cmdNames.symbols = settings.symbols.value;
1024bda5b58fSmrg        cmdNames.keycodes = settings.keycodes.value;
1025bda5b58fSmrg        cmdNames.geometry = settings.geometry.value;
1026bda5b58fSmrg        cmdNames.keymap = settings.keymap.value;
10271568b75bSmrg        xkb = XkbGetKeyboardByName(dpy, deviceSpec, &cmdNames,
10281568b75bSmrg                                   XkbGBN_AllComponentsMask,
10291568b75bSmrg                                   XkbGBN_AllComponentsMask &
10301568b75bSmrg                                   (~XkbGBN_GeometryMask), True);
10311568b75bSmrg        if (!xkb)
10321568b75bSmrg        {
10331568b75bSmrg            ERR("Error loading new keyboard description\n");
10341568b75bSmrg            return False;
10351568b75bSmrg        }
10361568b75bSmrg        /* update the XKB root property */
1037bda5b58fSmrg        if (settings.rules.value && (rdefs.model || rdefs.layout))
10381568b75bSmrg        {
1039bda5b58fSmrg            if (!XkbRF_SetNamesProp(dpy, settings.rules.value, &rdefs))
10401568b75bSmrg            {
10411568b75bSmrg                VMSG(0, "Error updating the XKB names property\n");
10421568b75bSmrg            }
10431568b75bSmrg        }
10441568b75bSmrg    }
10451568b75bSmrg    if (print)
10461568b75bSmrg    {
10477d5e3a19Smrg        printKeymap();
10487d5e3a19Smrg    }
1049765486e8Smrg    if (query)
1050765486e8Smrg    {
1051bda5b58fSmrg        dumpNames(True, False);
1052765486e8Smrg    }
10537d5e3a19Smrg    return True;
10547d5e3a19Smrg}
10557d5e3a19Smrg
10567d5e3a19Smrg
10577d5e3a19Smrgint
10581568b75bSmrgmain(int argc, char **argv)
10597d5e3a19Smrg{
10601568b75bSmrg    if ((!parseArgs(argc, argv)) || (!getDisplay(argc, argv)))
10611568b75bSmrg        exit(-1);
1062bda5b58fSmrg    settings.locale.value = setlocale(LC_ALL, settings.locale.value);
1063bda5b58fSmrg    settings.locale.src = FROM_SERVER;
1064bda5b58fSmrg    VMSG1(7, "locale is %s\n", settings.locale.value);
10657d5e3a19Smrg    if (dpy)
10667d5e3a19Smrg        getServerValues();
1067bda5b58fSmrg    if (settings.config.value && (!applyConfig(settings.config.value)))
10681568b75bSmrg        exit(-3);
10697d5e3a19Smrg    if (!applyRules())
10701568b75bSmrg        exit(-4);
10717d5e3a19Smrg    if (!applyComponentNames())
10721568b75bSmrg        exit(-5);
10737d5e3a19Smrg    if (dpy)
10741568b75bSmrg        XCloseDisplay(dpy);
10757d5e3a19Smrg    exit(0);
10767d5e3a19Smrg}
1077