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
27b8414663Smrg#ifdef HAVE_CONFIG_H
28b8414663Smrg#include "config.h"
29b8414663Smrg#endif
307d5e3a19Smrg#include <stdio.h>
317d5e3a19Smrg#include <stdlib.h>
327d5e3a19Smrg#include <locale.h>
337d5e3a19Smrg#include <limits.h>
347d5e3a19Smrg#include <ctype.h>
357d5e3a19Smrg#include <X11/Xlib.h>
367d5e3a19Smrg#include <X11/Xos.h>
377d5e3a19Smrg#include <X11/XKBlib.h>
387d5e3a19Smrg#include <X11/extensions/XKBfile.h>
397d5e3a19Smrg#include <X11/extensions/XKBconfig.h>
407d5e3a19Smrg#include <X11/extensions/XKBrules.h>
415212f86fSmrg#include <X11/extensions/Xrandr.h>
427d5e3a19Smrg
437d5e3a19Smrg#ifndef PATH_MAX
447d5e3a19Smrg#ifdef MAXPATHLEN
457d5e3a19Smrg#define PATH_MAX MAXPATHLEN
467d5e3a19Smrg#else
477d5e3a19Smrg#define PATH_MAX 1024
487d5e3a19Smrg#endif
497d5e3a19Smrg#endif
507d5e3a19Smrg
517d5e3a19Smrg#ifndef DFLT_XKB_CONFIG_ROOT
52bda5b58fSmrg#define DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb"
537d5e3a19Smrg#endif
547d5e3a19Smrg#ifndef DFLT_XKB_RULES_FILE
55bda5b58fSmrg#define DFLT_XKB_RULES_FILE "base"
567d5e3a19Smrg#endif
577d5e3a19Smrg#ifndef DFLT_XKB_LAYOUT
58bda5b58fSmrg#define DFLT_XKB_LAYOUT "us"
597d5e3a19Smrg#endif
607d5e3a19Smrg#ifndef DFLT_XKB_MODEL
61bda5b58fSmrg#define DFLT_XKB_MODEL "pc105"
627d5e3a19Smrg#endif
637d5e3a19Smrg
64bda5b58fSmrg/* Constants to state how a value was obtained. The order of these
651568b75bSmrg * is important, the bigger the higher the priority.
661568b75bSmrg * e.g. FROM_CONFIG overrides FROM_SERVER */
67bda5b58fSmrgenum source {
68bda5b58fSmrg    UNDEFINED = 0,
69bda5b58fSmrg    FROM_SERVER,          /* Retrieved from server at runtime. */
70bda5b58fSmrg    FROM_RULES,           /* Xkb rules file. */
71bda5b58fSmrg    FROM_CONFIG,          /* Command-line specified config file. */
72bda5b58fSmrg    FROM_CMD_LINE,        /* Specified at the cmdline. */
73bda5b58fSmrg    NUM_SOURCES
74bda5b58fSmrg};
757d5e3a19Smrg
767d5e3a19Smrg/***====================================================================***/
771568b75bSmrgstatic Bool print = False;
78765486e8Smrgstatic Bool query = False;
791568b75bSmrgstatic Bool synch = False;
801568b75bSmrgstatic int verbose = 5;
811568b75bSmrg
821568b75bSmrgstatic Display *dpy;
831568b75bSmrg
841568b75bSmrg/**
851568b75bSmrg * human-readable versions of FROM_CONFIG, FROM_SERVER, etc. Used for error
861568b75bSmrg * reporting.
871568b75bSmrg */
88bda5b58fSmrgstatic const char *srcName[NUM_SOURCES] = {
891568b75bSmrg    "undefined", "X server", "rules file", "config file", "command line"
907d5e3a19Smrg};
917d5e3a19Smrg
92bda5b58fSmrgstruct setting {
93bda5b58fSmrg    char const  *name;  /* Human-readable setting name. Used for error reporting. */
94bda5b58fSmrg    char        *value; /* Holds the value. */
95bda5b58fSmrg    enum source  src;   /* Holds the source. */
96bda5b58fSmrg};
97bda5b58fSmrg
98bda5b58fSmrgtypedef struct setting setting_t;
99bda5b58fSmrg
100bda5b58fSmrgstruct settings {
101bda5b58fSmrg    setting_t rules;     /* Rules file */
102bda5b58fSmrg    setting_t config;    /* Config file (if used) */
103bda5b58fSmrg    setting_t display;   /* X display name */
104bda5b58fSmrg    setting_t locale;    /* Machine's locale */
105bda5b58fSmrg    setting_t model;
106bda5b58fSmrg    setting_t layout;
107bda5b58fSmrg    setting_t variant;
108bda5b58fSmrg    setting_t keycodes;
109bda5b58fSmrg    setting_t types;
110bda5b58fSmrg    setting_t compat;
111bda5b58fSmrg    setting_t symbols;
112bda5b58fSmrg    setting_t geometry;
113bda5b58fSmrg    setting_t keymap;
114bda5b58fSmrg};
115bda5b58fSmrg
116bda5b58fSmrgtypedef struct settings settings_t;
117bda5b58fSmrg
118bda5b58fSmrgstatic settings_t settings = {
119bda5b58fSmrg    { "rules file",         NULL, UNDEFINED },
120bda5b58fSmrg    { "config file",        NULL, UNDEFINED },
121bda5b58fSmrg    { "X display",          NULL, UNDEFINED },
122bda5b58fSmrg    { "locale",             NULL, UNDEFINED },
123bda5b58fSmrg    { "keyboard model",     NULL, UNDEFINED },
124bda5b58fSmrg    { "keyboard layout",    NULL, UNDEFINED },
125bda5b58fSmrg    { "layout variant",     NULL, UNDEFINED },
126bda5b58fSmrg    { "keycodes",           NULL, UNDEFINED },
127bda5b58fSmrg    { "types",              NULL, UNDEFINED },
128bda5b58fSmrg    { "compatibility map",  NULL, UNDEFINED },
129bda5b58fSmrg    { "symbols",            NULL, UNDEFINED },
130bda5b58fSmrg    { "geometry",           NULL, UNDEFINED },
131bda5b58fSmrg    { "keymap",             NULL, UNDEFINED }
1327d5e3a19Smrg};
1337d5e3a19Smrg
1341568b75bSmrgstatic XkbConfigRtrnRec cfgResult;
1357d5e3a19Smrg
1361568b75bSmrgstatic XkbRF_VarDefsRec rdefs;
1377d5e3a19Smrg
1381568b75bSmrgstatic Bool clearOptions = False;
1397d5e3a19Smrg
140bda5b58fSmrgstruct list {
141bda5b58fSmrg    char  **item;   /* Array of items. */
142bda5b58fSmrg    int     sz;     /* Size of array. */
143bda5b58fSmrg    int     num;    /* Number of used elements. */
144bda5b58fSmrg};
145bda5b58fSmrg
146bda5b58fSmrgtypedef struct list list_t;
147bda5b58fSmrg
148bda5b58fSmrgstatic list_t options = { NULL, 0, 0 };
149bda5b58fSmrg
150bda5b58fSmrgstatic list_t inclPath = { NULL, 0, 0 };
1517d5e3a19Smrg
1521568b75bSmrgstatic XkbDescPtr xkb = NULL;
1537d5e3a19Smrg
1541568b75bSmrgstatic int deviceSpec = XkbUseCoreKbd;
1557d5e3a19Smrg
1567d5e3a19Smrg/***====================================================================***/
1577d5e3a19Smrg
158bda5b58fSmrg#define streq(s1,s2)    (strcmp(s1,s2)==0)
159bda5b58fSmrg#define strpfx(s1,s2)   (strncmp(s1,s2,strlen(s2))==0)
160bda5b58fSmrg
161bda5b58fSmrg#define MSG(s)          printf(s)
162bda5b58fSmrg#define MSG1(s,a)       printf(s,a)
163bda5b58fSmrg#define MSG2(s,a,b)     printf(s,a,b)
164bda5b58fSmrg#define MSG3(s,a,b,c)   printf(s,a,b,c)
1657d5e3a19Smrg
166bda5b58fSmrg#define VMSG(l,s)        if (verbose>(l)) printf(s)
167bda5b58fSmrg#define VMSG1(l,s,a)     if (verbose>(l)) printf(s,a)
168bda5b58fSmrg#define VMSG2(l,s,a,b)   if (verbose>(l)) printf(s,a,b)
169bda5b58fSmrg#define VMSG3(l,s,a,b,c) if (verbose>(l)) printf(s,a,b,c)
1707d5e3a19Smrg
171bda5b58fSmrg#define ERR(s)          fprintf(stderr,s)
172bda5b58fSmrg#define ERR1(s,a)       fprintf(stderr,s,a)
173bda5b58fSmrg#define ERR2(s,a,b)     fprintf(stderr,s,a,b)
174bda5b58fSmrg#define ERR3(s,a,b,c)   fprintf(stderr,s,a,b,c)
1757d5e3a19Smrg
1765212f86fSmrg#define OOM(ptr)        do { if ((ptr) == NULL) { ERR("Out of memory.\n"); exit(1); } } while (0)
1777d5e3a19Smrg
1787d5e3a19Smrg/***====================================================================***/
1797d5e3a19Smrg
1805212f86fSmrgstatic Bool addToList(list_t *list, const char *newVal);
1815212f86fSmrgstatic void usage(int argc, char **argv);
1825212f86fSmrgstatic void dumpNames(Bool wantRules, Bool wantCNames);
1835212f86fSmrgstatic void trySetString(setting_t * setting, char *newVal, enum source src);
1845212f86fSmrgstatic Bool setOptString(int *arg, int argc, char **argv, setting_t *setting, enum source src);
1855212f86fSmrgstatic int parseArgs(int argc, char **argv);
1865212f86fSmrgstatic Bool getDisplay(int argc, char **argv);
1875212f86fSmrgstatic Bool getServerValues(void);
1885212f86fSmrgstatic FILE *findFileInPath(char *name);
1895212f86fSmrgstatic Bool addStringToOptions(char *opt_str, list_t *opts);
1905212f86fSmrgstatic char *stringFromOptions(char *orig, list_t *newOpts);
1915212f86fSmrgstatic Bool applyConfig(char *name);
1925212f86fSmrgstatic XkbRF_RulesPtr tryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules);
1935212f86fSmrgstatic Bool applyRules(void);
1945212f86fSmrgstatic Bool applyComponentNames(void);
1955212f86fSmrgstatic void printKeymap(void);
1967d5e3a19Smrg
1977d5e3a19Smrg/***====================================================================***/
1987d5e3a19Smrg
199bda5b58fSmrg/*
200bda5b58fSmrg    If newVal is NULL or empty string, the list is cleared.
201bda5b58fSmrg    Otherwise newVal is added to the end of the list (if it is not present in the list yet).
202bda5b58fSmrg*/
203bda5b58fSmrg
2045212f86fSmrgstatic Bool
205bda5b58fSmrgaddToList(list_t *list, const char *newVal)
2067d5e3a19Smrg{
2071568b75bSmrg    register int i;
2081568b75bSmrg
2091568b75bSmrg    if ((!newVal) || (!newVal[0]))
2101568b75bSmrg    {
211bda5b58fSmrg        list->num = 0;
2121568b75bSmrg        return True;
2131568b75bSmrg    }
214bda5b58fSmrg    for (i = 0; i < list->num; i++)
2151568b75bSmrg    {
216bda5b58fSmrg        if (streq(list->item[i], newVal))
2171568b75bSmrg            return True;
2181568b75bSmrg    }
219bda5b58fSmrg    if ((list->item == NULL) || (list->sz < 1))
2201568b75bSmrg    {
221bda5b58fSmrg        list->num = 0;
222bda5b58fSmrg        list->sz = 4;
223bda5b58fSmrg        list->item = (char **) calloc(list->sz, sizeof(char *));
224bda5b58fSmrg        OOM(list->item);
2251568b75bSmrg    }
226bda5b58fSmrg    else if (list->num >= list->sz)
2271568b75bSmrg    {
228bda5b58fSmrg        list->sz *= 2;
229bda5b58fSmrg        list->item = (char **) realloc(list->item, (list->sz) * sizeof(char *));
230bda5b58fSmrg        OOM(list->item);
2311568b75bSmrg    }
232bda5b58fSmrg    list->item[list->num] = strdup(newVal);
233bda5b58fSmrg    OOM(list->item[list->num]);
234bda5b58fSmrg    list->num += 1;
2357d5e3a19Smrg    return True;
2367d5e3a19Smrg}
2377d5e3a19Smrg
2387d5e3a19Smrg/***====================================================================***/
2397d5e3a19Smrg
2405212f86fSmrgstatic void
2411568b75bSmrgusage(int argc, char **argv)
2427d5e3a19Smrg{
243bda5b58fSmrg    MSG1(
244bda5b58fSmrg        "Usage: %s [options] [<layout> [<variant> [<option> ... ]]]\n"
245bda5b58fSmrg        "Options:\n"
246bda5b58fSmrg        "  -?, -help           Print this message\n"
247bda5b58fSmrg        "  -compat <name>      Specifies compatibility map component name\n"
248bda5b58fSmrg        "  -config <file>      Specifies configuration file to use\n"
249bda5b58fSmrg        "  -device <deviceid>  Specifies the device ID to use\n"
250bda5b58fSmrg        "  -display <dpy>      Specifies display to use\n"
251bda5b58fSmrg        "  -geometry <name>    Specifies geometry component name\n"
252b8414663Smrg        "  -I <dir>            Add <dir> to list of directories to be used\n"
253bda5b58fSmrg        "  -keycodes <name>    Specifies keycodes component name\n"
254bda5b58fSmrg        "  -keymap <name>      Specifies name of keymap to load\n"
255bda5b58fSmrg        "  -layout <name>      Specifies layout used to choose component names\n"
256bda5b58fSmrg        "  -model <name>       Specifies model used to choose component names\n"
257bda5b58fSmrg        "  -option <name>      Adds an option used to choose component names\n"
258bda5b58fSmrg        "  -print              Print a complete xkb_keymap description and exit\n"
259bda5b58fSmrg        "  -query              Print the current layout settings and exit\n"
260bda5b58fSmrg        "  -rules <name>       Name of rules file to use\n"
261bda5b58fSmrg        "  -symbols <name>     Specifies symbols component name\n"
262bda5b58fSmrg        "  -synch              Synchronize request with X server\n"
263bda5b58fSmrg        "  -types <name>       Specifies types component name\n"
264b8414663Smrg        "  -v[erbose] [<lvl>]  Sets verbosity (1..10); higher values yield more messages\n"
265b8414663Smrg        "  -version            Print the program's version number\n"
266bda5b58fSmrg        "  -variant <name>     Specifies layout variant used to choose component names\n",
267bda5b58fSmrg        argv[0]
268bda5b58fSmrg    );
2697d5e3a19Smrg}
2707d5e3a19Smrg
2715212f86fSmrgstatic void
2721568b75bSmrgdumpNames(Bool wantRules, Bool wantCNames)
2737d5e3a19Smrg{
2741568b75bSmrg    if (wantRules)
2751568b75bSmrg    {
276bda5b58fSmrg        if (settings.rules.value)
277bda5b58fSmrg            MSG1("rules:      %s\n", settings.rules.value);
278bda5b58fSmrg        if (settings.model.value)
279bda5b58fSmrg            MSG1("model:      %s\n", settings.model.value);
280bda5b58fSmrg        if (settings.layout.value)
281bda5b58fSmrg            MSG1("layout:     %s\n", settings.layout.value);
282bda5b58fSmrg        if (settings.variant.value)
283bda5b58fSmrg            MSG1("variant:    %s\n", settings.variant.value);
284bda5b58fSmrg        if (options.item)
2851568b75bSmrg        {
286bda5b58fSmrg            char *opt_str = stringFromOptions(NULL, &options);
2871568b75bSmrg            MSG1("options:    %s\n", opt_str);
2881568b75bSmrg            free(opt_str);
2891568b75bSmrg        }
2901568b75bSmrg    }
2911568b75bSmrg    if (wantCNames)
2921568b75bSmrg    {
293bda5b58fSmrg        if (settings.keymap.value)
294bda5b58fSmrg            MSG1("keymap:     %s\n", settings.keymap.value);
295bda5b58fSmrg        if (settings.keycodes.value)
296bda5b58fSmrg            MSG1("keycodes:   %s\n", settings.keycodes.value);
297bda5b58fSmrg        if (settings.types.value)
298bda5b58fSmrg            MSG1("types:      %s\n", settings.types.value);
299bda5b58fSmrg        if (settings.compat.value)
300bda5b58fSmrg            MSG1("compat:     %s\n", settings.compat.value);
301bda5b58fSmrg        if (settings.symbols.value)
302bda5b58fSmrg            MSG1("symbols:    %s\n", settings.symbols.value);
303bda5b58fSmrg        if (settings.geometry.value)
304bda5b58fSmrg            MSG1("geometry:   %s\n", settings.geometry.value);
3057d5e3a19Smrg    }
3067d5e3a19Smrg    return;
3077d5e3a19Smrg}
3087d5e3a19Smrg
3097d5e3a19Smrg/***====================================================================***/
3107d5e3a19Smrg
3111568b75bSmrg/**
3121568b75bSmrg * Set the given string (obtained from src) in the svValue/svSrc globals.
3131568b75bSmrg * If the given item is already set, it is overridden if the original source
3141568b75bSmrg * is less significant than the given one.
3151568b75bSmrg *
3161568b75bSmrg * @param which What value is it (one of RULES_NDX, CONFIG_NDX, ...)
3171568b75bSmrg */
3185212f86fSmrgstatic void
319bda5b58fSmrgtrySetString(setting_t *setting, char *newVal, enum source src)
3207d5e3a19Smrg{
321bda5b58fSmrg    if (setting->value != NULL)
3221568b75bSmrg    {
323bda5b58fSmrg        if (setting->src == src)
3241568b75bSmrg        {
3251568b75bSmrg            VMSG2(0, "Warning! More than one %s from %s\n",
326bda5b58fSmrg                  setting->name, srcName[src]);
3271568b75bSmrg            VMSG2(0, "         Using \"%s\", ignoring \"%s\"\n",
328bda5b58fSmrg                  setting->value, newVal);
3291568b75bSmrg            return;
3301568b75bSmrg        }
331bda5b58fSmrg        else if (setting->src > src)
3321568b75bSmrg        {
333bda5b58fSmrg            VMSG1(5, "Warning! Multiple definitions of %s\n", setting->name);
3341568b75bSmrg            VMSG2(5, "         Using %s, ignoring %s\n",
335bda5b58fSmrg                  srcName[setting->src], srcName[src]);
3361568b75bSmrg            return;
3371568b75bSmrg        }
3381568b75bSmrg    }
339bda5b58fSmrg    setting->src = src;
340bda5b58fSmrg    setting->value = newVal;
3417d5e3a19Smrg    return;
3427d5e3a19Smrg}
3437d5e3a19Smrg
3445212f86fSmrgstatic Bool
345bda5b58fSmrgsetOptString(int *arg, int argc, char **argv, setting_t *setting, enum source src)
3467d5e3a19Smrg{
3471568b75bSmrg    int ndx;
3481568b75bSmrg    char *opt;
3491568b75bSmrg
3501568b75bSmrg    ndx = *arg;
3511568b75bSmrg    opt = argv[ndx];
3521568b75bSmrg    if (ndx >= argc - 1)
3531568b75bSmrg    {
354bda5b58fSmrg        VMSG1(0, "No %s specified on the command line\n", setting->name);
3551568b75bSmrg        VMSG1(0, "Trailing %s option ignored\n", opt);
3561568b75bSmrg        return True;
3577d5e3a19Smrg    }
3587d5e3a19Smrg    ndx++;
3591568b75bSmrg    *arg = ndx;
360bda5b58fSmrg    if (setting->value != NULL)
3611568b75bSmrg    {
362bda5b58fSmrg        if (setting->src == src)
3631568b75bSmrg        {
364bda5b58fSmrg            VMSG2(0, "More than one %s on %s\n", setting->name, srcName[src]);
365bda5b58fSmrg            VMSG2(0, "Using \"%s\", ignoring \"%s\"\n", setting->value,
3661568b75bSmrg                  argv[ndx]);
3671568b75bSmrg            return True;
3681568b75bSmrg        }
369bda5b58fSmrg        else if (setting->src > src)
3701568b75bSmrg        {
371bda5b58fSmrg            VMSG1(5, "Multiple definitions of %s\n", setting->name);
372bda5b58fSmrg            VMSG2(5, "Using %s, ignoring %s\n", srcName[setting->src],
3731568b75bSmrg                  srcName[src]);
3741568b75bSmrg            return True;
3751568b75bSmrg        }
3761568b75bSmrg    }
377bda5b58fSmrg    setting->src = src;
378bda5b58fSmrg    setting->value = argv[ndx];
3797d5e3a19Smrg    return True;
3807d5e3a19Smrg}
3817d5e3a19Smrg
3827d5e3a19Smrg/***====================================================================***/
3837d5e3a19Smrg
3841568b75bSmrg/**
3851568b75bSmrg * Parse commandline arguments.
3861568b75bSmrg * Return True on success or False if an unrecognized option has been
3871568b75bSmrg * specified.
3881568b75bSmrg */
3895212f86fSmrgstatic int
3901568b75bSmrgparseArgs(int argc, char **argv)
3917d5e3a19Smrg{
3921568b75bSmrg    int i;
3931568b75bSmrg    Bool ok;
3941568b75bSmrg    unsigned present;
3951568b75bSmrg
3961568b75bSmrg    ok = True;
397bda5b58fSmrg    addToList(&inclPath, ".");
398bda5b58fSmrg    addToList(&inclPath, DFLT_XKB_CONFIG_ROOT);
3991568b75bSmrg    for (i = 1; (i < argc) && ok; i++)
4001568b75bSmrg    {
4011568b75bSmrg        if (argv[i][0] != '-')
4021568b75bSmrg        {
4031568b75bSmrg            /* Allow a call like "setxkbmap us" to work. Layout is default,
4041568b75bSmrg               if -layout is given, then try parsing variant, then options */
405bda5b58fSmrg            if (!settings.layout.src)
406bda5b58fSmrg                trySetString(&settings.layout, argv[i], FROM_CMD_LINE);
407bda5b58fSmrg            else if (!settings.variant.src)
408bda5b58fSmrg                trySetString(&settings.variant, argv[i], FROM_CMD_LINE);
4091568b75bSmrg            else
410bda5b58fSmrg                ok = addToList(&options, argv[i]);
4111568b75bSmrg        }
4121568b75bSmrg        else if (streq(argv[i], "-compat"))
413bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.compat, FROM_CMD_LINE);
4141568b75bSmrg        else if (streq(argv[i], "-config"))
415bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.config, FROM_CMD_LINE);
4161568b75bSmrg        else if (streq(argv[i], "-device"))
417bda5b58fSmrg        {
418bda5b58fSmrg            if ( ++i < argc ) {
419bda5b58fSmrg                deviceSpec = atoi(argv[i]); /* only allow device IDs, not names */
420bda5b58fSmrg            } else {
421bda5b58fSmrg                usage(argc, argv);
4225212f86fSmrg                exit(1);
423bda5b58fSmrg            }
424bda5b58fSmrg        }
4251568b75bSmrg        else if (streq(argv[i], "-display"))
426bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.display, FROM_CMD_LINE);
4271568b75bSmrg        else if (streq(argv[i], "-geometry"))
428bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.geometry, FROM_CMD_LINE);
4291568b75bSmrg        else if (streq(argv[i], "-help") || streq(argv[i], "-?"))
4301568b75bSmrg        {
4311568b75bSmrg            usage(argc, argv);
4321568b75bSmrg            exit(0);
4331568b75bSmrg        }
434bda5b58fSmrg        else if (streq(argv[i], "-I")) /* space between -I and path */
435bda5b58fSmrg        {
436bda5b58fSmrg            if ( ++i < argc )
437bda5b58fSmrg                ok = addToList(&inclPath, argv[i]);
438bda5b58fSmrg            else
439bda5b58fSmrg                VMSG(0, "No directory specified on the command line\n"
440bda5b58fSmrg                     "Trailing -I option ignored\n");
441bda5b58fSmrg        }
442bda5b58fSmrg        else if (strpfx(argv[i], "-I")) /* no space between -I and path */
443bda5b58fSmrg            ok = addToList(&inclPath, &argv[i][2]);
4441568b75bSmrg        else if (streq(argv[i], "-keycodes"))
445bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.keycodes, FROM_CMD_LINE);
4461568b75bSmrg        else if (streq(argv[i], "-keymap"))
447bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.keymap, FROM_CMD_LINE);
4481568b75bSmrg        else if (streq(argv[i], "-layout"))
449bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.layout, FROM_CMD_LINE);
4501568b75bSmrg        else if (streq(argv[i], "-model"))
451bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.model, FROM_CMD_LINE);
4521568b75bSmrg        else if (streq(argv[i], "-option"))
4531568b75bSmrg        {
4541568b75bSmrg            if ((i == argc - 1) || (argv[i + 1][0] == '\0')
4551568b75bSmrg                || (argv[i + 1][0] == '-'))
4561568b75bSmrg            {
4571568b75bSmrg                clearOptions = True;
458bda5b58fSmrg                ok = addToList(&options, "");
4591568b75bSmrg                if (i < argc - 1 && argv[i + 1][0] == '\0')
4601568b75bSmrg                    i++;
4611568b75bSmrg            }
4621568b75bSmrg            else
4631568b75bSmrg            {
464bda5b58fSmrg                ok = addToList(&options, argv[++i]);
4651568b75bSmrg            }
4661568b75bSmrg        }
4671568b75bSmrg        else if (streq(argv[i], "-print"))
4681568b75bSmrg            print = True;
469765486e8Smrg        else if (streq(argv[i], "-query"))
470765486e8Smrg            query = True;
4711568b75bSmrg        else if (streq(argv[i], "-rules"))
472bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.rules, FROM_CMD_LINE);
4731568b75bSmrg        else if (streq(argv[i], "-symbols"))
474bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.symbols, FROM_CMD_LINE);
4751568b75bSmrg        else if (streq(argv[i], "-synch"))
4761568b75bSmrg            synch = True;
4771568b75bSmrg        else if (streq(argv[i], "-types"))
478bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.types, FROM_CMD_LINE);
479b8414663Smrg        else if (streq(argv[i], "-version"))
480b8414663Smrg        {
481b8414663Smrg            MSG1("setxkbmap %s\n", PACKAGE_VERSION);
482b8414663Smrg            exit(0);
483b8414663Smrg        }
4841568b75bSmrg        else if (streq(argv[i], "-verbose") || (streq(argv[i], "-v")))
4851568b75bSmrg        {
4861568b75bSmrg            if ((i < argc - 1) && (isdigit(argv[i + 1][0])))
4871568b75bSmrg                verbose = atoi(argv[++i]);
4881568b75bSmrg            else
4891568b75bSmrg                verbose++;
4901568b75bSmrg            if (verbose < 0)
4911568b75bSmrg            {
4921568b75bSmrg                ERR1("Illegal verbose level %d.  Reset to 0\n", verbose);
4931568b75bSmrg                verbose = 0;
4941568b75bSmrg            }
4951568b75bSmrg            else if (verbose > 10)
4961568b75bSmrg            {
4971568b75bSmrg                ERR1("Illegal verbose level %d.  Reset to 10\n", verbose);
4981568b75bSmrg                verbose = 10;
4991568b75bSmrg            }
5001568b75bSmrg            VMSG1(7, "Setting verbose level to %d\n", verbose);
5011568b75bSmrg        }
5021568b75bSmrg        else if (streq(argv[i], "-variant"))
503bda5b58fSmrg            ok = setOptString(&i, argc, argv, &settings.variant, FROM_CMD_LINE);
5041568b75bSmrg        else
5051568b75bSmrg        {
5061568b75bSmrg            ERR1("Error!   Option \"%s\" not recognized\n", argv[i]);
5071568b75bSmrg            ok = False;
5081568b75bSmrg        }
5091568b75bSmrg    }
5101568b75bSmrg
5111568b75bSmrg    present = 0;
512bda5b58fSmrg    if (settings.types.value)
5131568b75bSmrg        present++;
514bda5b58fSmrg    if (settings.compat.value)
5151568b75bSmrg        present++;
516bda5b58fSmrg    if (settings.symbols.value)
5171568b75bSmrg        present++;
518bda5b58fSmrg    if (settings.keycodes.value)
5191568b75bSmrg        present++;
520bda5b58fSmrg    if (settings.geometry.value)
5211568b75bSmrg        present++;
522bda5b58fSmrg    if (settings.config.value)
5231568b75bSmrg        present++;
524bda5b58fSmrg    if (settings.model.value)
5251568b75bSmrg        present++;
526bda5b58fSmrg    if (settings.layout.value)
5271568b75bSmrg        present++;
528bda5b58fSmrg    if (settings.variant.value)
5291568b75bSmrg        present++;
530bda5b58fSmrg    if (settings.keymap.value && present)
5311568b75bSmrg    {
5321568b75bSmrg        ERR("No other components can be specified when a keymap is present\n");
5331568b75bSmrg        return False;
5347d5e3a19Smrg    }
5357d5e3a19Smrg    return ok;
5367d5e3a19Smrg}
5377d5e3a19Smrg
5381568b75bSmrg/**
5391568b75bSmrg * Open a connection to the display and print error if it fails.
5401568b75bSmrg *
5411568b75bSmrg * @return True on success or False otherwise.
5421568b75bSmrg */
5435212f86fSmrgstatic Bool
5441568b75bSmrggetDisplay(int argc, char **argv)
5457d5e3a19Smrg{
5461568b75bSmrg    int major, minor, why;
5471568b75bSmrg
5481568b75bSmrg    major = XkbMajorVersion;
5491568b75bSmrg    minor = XkbMinorVersion;
5501568b75bSmrg    dpy =
551bda5b58fSmrg        XkbOpenDisplay(settings.display.value, NULL, NULL, &major, &minor,
5521568b75bSmrg                       &why);
5531568b75bSmrg    if (!dpy)
5541568b75bSmrg    {
555bda5b58fSmrg        if (settings.display.value == NULL)
556bda5b58fSmrg            settings.display.value = getenv("DISPLAY");
557bda5b58fSmrg        if (settings.display.value == NULL)
5585212f86fSmrg            settings.display.value = (char *) "default display";
5591568b75bSmrg        switch (why)
5601568b75bSmrg        {
5611568b75bSmrg        case XkbOD_BadLibraryVersion:
5621568b75bSmrg            ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
5631568b75bSmrg                 XkbMajorVersion, XkbMinorVersion);
5641568b75bSmrg            ERR2("Xlib supports incompatible version %d.%02d\n",
5651568b75bSmrg                 major, minor);
5661568b75bSmrg            break;
5671568b75bSmrg        case XkbOD_ConnectionRefused:
568bda5b58fSmrg            ERR1("Cannot open display \"%s\"\n", settings.display.value);
5691568b75bSmrg            break;
5701568b75bSmrg        case XkbOD_NonXkbServer:
571bda5b58fSmrg            ERR1("XKB extension not present on %s\n", settings.display.value);
5721568b75bSmrg            break;
5731568b75bSmrg        case XkbOD_BadServerVersion:
5741568b75bSmrg            ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
5751568b75bSmrg                 XkbMajorVersion, XkbMinorVersion);
5761568b75bSmrg            ERR3("Server %s uses incompatible version %d.%02d\n",
577bda5b58fSmrg                 settings.display.value, major, minor);
5781568b75bSmrg            break;
5791568b75bSmrg        default:
5801568b75bSmrg            ERR1("Unknown error %d from XkbOpenDisplay\n", why);
5811568b75bSmrg            break;
5821568b75bSmrg        }
5831568b75bSmrg        return False;
5847d5e3a19Smrg    }
5857d5e3a19Smrg    if (synch)
5861568b75bSmrg        XSynchronize(dpy, True);
5877d5e3a19Smrg    return True;
5887d5e3a19Smrg}
5897d5e3a19Smrg
5907d5e3a19Smrg/***====================================================================***/
5917d5e3a19Smrg
5921568b75bSmrg/**
593765486e8Smrg * Retrieve xkb values from the XKB_RULES_NAMES property and store their
5941568b75bSmrg * contents in svValues.
5951568b75bSmrg * If the property cannot be read, the built-in defaults are used.
5961568b75bSmrg *
5971568b75bSmrg * @return True.
5981568b75bSmrg */
5995212f86fSmrgstatic Bool
6007d5e3a19SmrggetServerValues(void)
6017d5e3a19Smrg{
6021568b75bSmrg    XkbRF_VarDefsRec vd;
6031568b75bSmrg    char *tmp = NULL;
6047d5e3a19Smrg
6051568b75bSmrg    if (!XkbRF_GetNamesProp(dpy, &tmp, &vd) || !tmp)
6061568b75bSmrg    {
6071568b75bSmrg        VMSG1(3, "Couldn't interpret %s property\n", _XKB_RF_NAMES_PROP_ATOM);
6085212f86fSmrg        tmp = (char *) DFLT_XKB_RULES_FILE;
6095212f86fSmrg        vd.model = (char *) DFLT_XKB_MODEL;
6105212f86fSmrg        vd.layout = (char *) DFLT_XKB_LAYOUT;
6117d5e3a19Smrg        vd.variant = NULL;
6127d5e3a19Smrg        vd.options = NULL;
6131568b75bSmrg        VMSG3(3, "Use defaults: rules - '%s' model - '%s' layout - '%s'\n",
6141568b75bSmrg              tmp, vd.model, vd.layout);
6157d5e3a19Smrg    }
6167d5e3a19Smrg    if (tmp)
617bda5b58fSmrg        trySetString(&settings.rules, tmp, FROM_SERVER);
6187d5e3a19Smrg    if (vd.model)
619bda5b58fSmrg        trySetString(&settings.model, vd.model, FROM_SERVER);
6207d5e3a19Smrg    if (vd.layout)
621bda5b58fSmrg        trySetString(&settings.layout, vd.layout, FROM_SERVER);
6227d5e3a19Smrg    if (vd.variant)
623bda5b58fSmrg        trySetString(&settings.variant, vd.variant, FROM_SERVER);
6241568b75bSmrg    if ((vd.options) && (!clearOptions))
6251568b75bSmrg    {
626bda5b58fSmrg        addStringToOptions(vd.options, &options);
6271568b75bSmrg        XFree(vd.options);
6287d5e3a19Smrg    }
6297d5e3a19Smrg    return True;
6307d5e3a19Smrg}
6317d5e3a19Smrg
6327d5e3a19Smrg/***====================================================================***/
6337d5e3a19Smrg
6345212f86fSmrgstatic FILE *
635bda5b58fSmrgfindFileInPath(char *name)
6367d5e3a19Smrg{
6371568b75bSmrg    register int i;
6381568b75bSmrg    char buf[PATH_MAX];
6391568b75bSmrg    FILE *fp;
6401568b75bSmrg
6411568b75bSmrg    if (name[0] == '/')
6421568b75bSmrg    {
6431568b75bSmrg        fp = fopen(name, "r");
6441568b75bSmrg        if ((verbose > 7) || ((!fp) && (verbose > 0)))
6451568b75bSmrg            MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), name);
6461568b75bSmrg        return fp;
6471568b75bSmrg    }
648bda5b58fSmrg    for (i = 0; (i < inclPath.num); i++)
6491568b75bSmrg    {
650bda5b58fSmrg        if (snprintf(buf, PATH_MAX, "%s/%s", inclPath.item[i], name) >=
6511568b75bSmrg            PATH_MAX)
6521568b75bSmrg        {
653bda5b58fSmrg            VMSG2(0, "Path too long (%s/%s). Ignored.\n", inclPath.item[i],
654bda5b58fSmrg                  name);
6551568b75bSmrg            continue;
6561568b75bSmrg        }
657bda5b58fSmrg        fp = fopen(buf, "r");
6581568b75bSmrg        if ((verbose > 7) || ((!fp) && (verbose > 5)))
6591568b75bSmrg            MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), buf);
6601568b75bSmrg        if (fp != NULL)
6611568b75bSmrg            return fp;
6627d5e3a19Smrg    }
6637d5e3a19Smrg    return NULL;
6647d5e3a19Smrg}
6657d5e3a19Smrg
6667d5e3a19Smrg/***====================================================================***/
6677d5e3a19Smrg
6685212f86fSmrgstatic Bool
669bda5b58fSmrgaddStringToOptions(char *opt_str, list_t *opts)
6707d5e3a19Smrg{
6711568b75bSmrg    char *tmp, *str, *next;
6721568b75bSmrg    Bool ok = True;
6737d5e3a19Smrg
674bda5b58fSmrg    str = strdup(opt_str);
675bda5b58fSmrg    OOM(str);
676bda5b58fSmrg    for (tmp = str; (tmp && *tmp != '\0') && ok; tmp = next)
6771568b75bSmrg    {
6781568b75bSmrg        next = strchr(str, ',');
6791568b75bSmrg        if (next)
6801568b75bSmrg        {
6811568b75bSmrg            *next = '\0';
6821568b75bSmrg            next++;
6831568b75bSmrg        }
684bda5b58fSmrg        ok = addToList(opts, tmp) && ok;
6857d5e3a19Smrg    }
6867d5e3a19Smrg    free(str);
6877d5e3a19Smrg    return ok;
6887d5e3a19Smrg}
6897d5e3a19Smrg
6907d5e3a19Smrg/***====================================================================***/
6917d5e3a19Smrg
6925212f86fSmrgstatic char *
693bda5b58fSmrgstringFromOptions(char *orig, list_t *newOpts)
6947d5e3a19Smrg{
695b8414663Smrg    size_t len;
696b8414663Smrg    int i, nOut;
6971568b75bSmrg
6981568b75bSmrg    if (orig)
6991568b75bSmrg        len = strlen(orig) + 1;
7001568b75bSmrg    else
7011568b75bSmrg        len = 0;
702bda5b58fSmrg    for (i = 0; i < newOpts->num; i++)
7031568b75bSmrg    {
704bda5b58fSmrg        if (newOpts->item[i])
705bda5b58fSmrg            len += strlen(newOpts->item[i]) + 1;
7061568b75bSmrg    }
7071568b75bSmrg    if (len < 1)
7081568b75bSmrg        return NULL;
7091568b75bSmrg    if (orig)
7101568b75bSmrg    {
7111568b75bSmrg        orig = (char *) realloc(orig, len);
712bda5b58fSmrg        OOM(orig);
7131568b75bSmrg        nOut = 1;
7141568b75bSmrg    }
7151568b75bSmrg    else
7161568b75bSmrg    {
7171568b75bSmrg        orig = (char *) calloc(len, 1);
718bda5b58fSmrg        OOM(orig);
7191568b75bSmrg        nOut = 0;
7201568b75bSmrg    }
721bda5b58fSmrg    for (i = 0; i < newOpts->num; i++)
7221568b75bSmrg    {
723bda5b58fSmrg        if (!newOpts->item[i])
7241568b75bSmrg            continue;
7251568b75bSmrg        if (nOut > 0)
7261568b75bSmrg        {
7271568b75bSmrg            strcat(orig, ",");
728bda5b58fSmrg            strcat(orig, newOpts->item[i]);
7291568b75bSmrg        }
7301568b75bSmrg        else
731bda5b58fSmrg            strcpy(orig, newOpts->item[i]);
7321568b75bSmrg        nOut++;
7337d5e3a19Smrg    }
7347d5e3a19Smrg    return orig;
7357d5e3a19Smrg}
7367d5e3a19Smrg
7377d5e3a19Smrg/***====================================================================***/
7387d5e3a19Smrg
7395212f86fSmrgstatic Bool
7407d5e3a19SmrgapplyConfig(char *name)
7417d5e3a19Smrg{
7421568b75bSmrg    FILE *fp;
7431568b75bSmrg    Bool ok;
7441568b75bSmrg
745bda5b58fSmrg    if ((fp = findFileInPath(name)) == NULL)
74613e6bc1cSmrg    {
74713e6bc1cSmrg        ERR1("Couldn't find configuration file \"%s\"\n", name);
7481568b75bSmrg        return False;
74913e6bc1cSmrg    }
7501568b75bSmrg    ok = XkbCFParse(fp, XkbCFDflts, NULL, &cfgResult);
7517d5e3a19Smrg    fclose(fp);
7521568b75bSmrg    if (!ok)
7531568b75bSmrg    {
75413e6bc1cSmrg        ERR1("Couldn't parse configuration file \"%s\"\n", name);
7551568b75bSmrg        return False;
7567d5e3a19Smrg    }
7571568b75bSmrg    if (cfgResult.rules_file)
7581568b75bSmrg    {
759bda5b58fSmrg        trySetString(&settings.rules, cfgResult.rules_file, FROM_CONFIG);
7601568b75bSmrg        cfgResult.rules_file = NULL;
7617d5e3a19Smrg    }
7621568b75bSmrg    if (cfgResult.model)
7631568b75bSmrg    {
764bda5b58fSmrg        trySetString(&settings.model, cfgResult.model, FROM_CONFIG);
7651568b75bSmrg        cfgResult.model = NULL;
7667d5e3a19Smrg    }
7671568b75bSmrg    if (cfgResult.layout)
7681568b75bSmrg    {
769bda5b58fSmrg        trySetString(&settings.layout, cfgResult.layout, FROM_CONFIG);
7701568b75bSmrg        cfgResult.layout = NULL;
7717d5e3a19Smrg    }
7721568b75bSmrg    if (cfgResult.variant)
7731568b75bSmrg    {
774bda5b58fSmrg        trySetString(&settings.variant, cfgResult.variant, FROM_CONFIG);
7751568b75bSmrg        cfgResult.variant = NULL;
7767d5e3a19Smrg    }
7771568b75bSmrg    if (cfgResult.options)
7781568b75bSmrg    {
779bda5b58fSmrg        addStringToOptions(cfgResult.options, &options);
7801568b75bSmrg        cfgResult.options = NULL;
7817d5e3a19Smrg    }
7821568b75bSmrg    if (cfgResult.keymap)
7831568b75bSmrg    {
784bda5b58fSmrg        trySetString(&settings.keymap, cfgResult.keymap, FROM_CONFIG);
7851568b75bSmrg        cfgResult.keymap = NULL;
7867d5e3a19Smrg    }
7871568b75bSmrg    if (cfgResult.keycodes)
7881568b75bSmrg    {
789bda5b58fSmrg        trySetString(&settings.keycodes, cfgResult.keycodes, FROM_CONFIG);
7901568b75bSmrg        cfgResult.keycodes = NULL;
7917d5e3a19Smrg    }
7921568b75bSmrg    if (cfgResult.geometry)
7931568b75bSmrg    {
794bda5b58fSmrg        trySetString(&settings.geometry, cfgResult.geometry, FROM_CONFIG);
7951568b75bSmrg        cfgResult.geometry = NULL;
7967d5e3a19Smrg    }
7971568b75bSmrg    if (cfgResult.symbols)
7981568b75bSmrg    {
799bda5b58fSmrg        trySetString(&settings.symbols, cfgResult.symbols, FROM_CONFIG);
8001568b75bSmrg        cfgResult.symbols = NULL;
8017d5e3a19Smrg    }
8021568b75bSmrg    if (cfgResult.types)
8031568b75bSmrg    {
804bda5b58fSmrg        trySetString(&settings.types, cfgResult.types, FROM_CONFIG);
8051568b75bSmrg        cfgResult.types = NULL;
8067d5e3a19Smrg    }
8071568b75bSmrg    if (cfgResult.compat)
8081568b75bSmrg    {
809bda5b58fSmrg        trySetString(&settings.compat, cfgResult.compat, FROM_CONFIG);
8101568b75bSmrg        cfgResult.compat = NULL;
8117d5e3a19Smrg    }
8121568b75bSmrg    if (verbose > 5)
8131568b75bSmrg    {
8141568b75bSmrg        MSG("After config file:\n");
8151568b75bSmrg        dumpNames(True, True);
8167d5e3a19Smrg    }
8177d5e3a19Smrg    return True;
8187d5e3a19Smrg}
8197d5e3a19Smrg
8205212f86fSmrgstatic XkbRF_RulesPtr
821bda5b58fSmrgtryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules)
822bda5b58fSmrg{
823bda5b58fSmrg    XkbRF_RulesPtr rules = NULL;
824bda5b58fSmrg    VMSG1(7, "Trying to load rules file %s...\n", name);
825bda5b58fSmrg    rules = XkbRF_Load(name, locale, wantDesc, wantRules);
826bda5b58fSmrg    if (rules)
827bda5b58fSmrg    {
828bda5b58fSmrg        VMSG(7, "Success.\n");
829bda5b58fSmrg    }
830bda5b58fSmrg    return rules;
831bda5b58fSmrg}
832bda5b58fSmrg
8331568b75bSmrg/**
8341568b75bSmrg * If any of model, layout, variant or options is specified, then compile the
8351568b75bSmrg * options into the
8361568b75bSmrg *
8371568b75bSmrg * @return True on success or false otherwise.
8381568b75bSmrg */
8395212f86fSmrgstatic Bool
8407d5e3a19SmrgapplyRules(void)
8417d5e3a19Smrg{
8421568b75bSmrg    int i;
8431568b75bSmrg    char *rfName;
844b8414663Smrg    XkbRF_RulesPtr rules = NULL;
8457d5e3a19Smrg
846bda5b58fSmrg    if (settings.model.src || settings.layout.src || settings.variant.src
847bda5b58fSmrg        || options.item)
8481568b75bSmrg    {
8491568b75bSmrg        char buf[PATH_MAX];
8501568b75bSmrg        XkbComponentNamesRec rnames;
8517d5e3a19Smrg
852bda5b58fSmrg        if (settings.variant.src < settings.layout.src)
853bda5b58fSmrg            settings.variant.value = NULL;
8547d5e3a19Smrg
855bda5b58fSmrg        rdefs.model = settings.model.value;
856bda5b58fSmrg        rdefs.layout = settings.layout.value;
857bda5b58fSmrg        rdefs.variant = settings.variant.value;
858bda5b58fSmrg        if (options.item)
8591568b75bSmrg            rdefs.options =
860bda5b58fSmrg                stringFromOptions(rdefs.options, &options);
8611568b75bSmrg
862bda5b58fSmrg        if (settings.rules.src)
863bda5b58fSmrg            rfName = settings.rules.value;
8641568b75bSmrg        else
8655212f86fSmrg            rfName = (char *) DFLT_XKB_RULES_FILE;
8661568b75bSmrg
8671568b75bSmrg        if (rfName[0] == '/')
8681568b75bSmrg        {
869bda5b58fSmrg            rules = tryLoadRules(rfName, settings.locale.value, True, True);
8701568b75bSmrg        }
8711568b75bSmrg        else
8721568b75bSmrg        {
8731568b75bSmrg            /* try to load rules files from all include paths until the first
8741568b75bSmrg             * we succeed with */
875bda5b58fSmrg            for (i = 0; (i < inclPath.num) && (!rules); i++)
8761568b75bSmrg            {
877bda5b58fSmrg                if (snprintf(buf, PATH_MAX, "%s/rules/%s",
878bda5b58fSmrg                             inclPath.item[i], rfName) >= PATH_MAX)
8791568b75bSmrg                {
8801568b75bSmrg                    VMSG2(0, "Path too long (%s/rules/%s). Ignored.\n",
881bda5b58fSmrg                          inclPath.item[i], rfName);
8821568b75bSmrg                    continue;
8831568b75bSmrg                }
884bda5b58fSmrg                rules = tryLoadRules(buf, settings.locale.value, True, True);
8851568b75bSmrg            }
8861568b75bSmrg        }
8871568b75bSmrg        if (!rules)
8881568b75bSmrg        {
889bda5b58fSmrg            ERR1("Couldn't find rules file (%s) \n", rfName);
8901568b75bSmrg            return False;
8911568b75bSmrg        }
8921568b75bSmrg        /* Let the rules file to the magic, then update the svValues with
8931568b75bSmrg         * those returned after processing the rules */
8941568b75bSmrg        XkbRF_GetComponents(rules, &rdefs, &rnames);
8951568b75bSmrg        if (rnames.keycodes)
8961568b75bSmrg        {
897bda5b58fSmrg            trySetString(&settings.keycodes, rnames.keycodes, FROM_RULES);
8981568b75bSmrg            rnames.keycodes = NULL;
8991568b75bSmrg        }
9001568b75bSmrg        if (rnames.symbols)
9011568b75bSmrg        {
902bda5b58fSmrg            trySetString(&settings.symbols, rnames.symbols, FROM_RULES);
9031568b75bSmrg            rnames.symbols = NULL;
9041568b75bSmrg        }
9051568b75bSmrg        if (rnames.types)
9061568b75bSmrg        {
907bda5b58fSmrg            trySetString(&settings.types, rnames.types, FROM_RULES);
9081568b75bSmrg            rnames.types = NULL;
9091568b75bSmrg        }
9101568b75bSmrg        if (rnames.compat)
9111568b75bSmrg        {
912bda5b58fSmrg            trySetString(&settings.compat, rnames.compat, FROM_RULES);
9131568b75bSmrg            rnames.compat = NULL;
9141568b75bSmrg        }
9151568b75bSmrg        if (rnames.geometry)
9161568b75bSmrg        {
917bda5b58fSmrg            trySetString(&settings.geometry, rnames.geometry, FROM_RULES);
9181568b75bSmrg            rnames.geometry = NULL;
9191568b75bSmrg        }
9201568b75bSmrg        if (rnames.keymap)
9211568b75bSmrg        {
922bda5b58fSmrg            trySetString(&settings.keymap, rnames.keymap, FROM_RULES);
9231568b75bSmrg            rnames.keymap = NULL;
9241568b75bSmrg        }
9251568b75bSmrg        if (verbose > 6)
9261568b75bSmrg        {
927bda5b58fSmrg            MSG1("Applied rules from %s:\n", rfName);
9281568b75bSmrg            dumpNames(True, False);
9291568b75bSmrg        }
9301568b75bSmrg    }
9311568b75bSmrg    else if (verbose > 6)
9321568b75bSmrg    {
9331568b75bSmrg        MSG("No rules variables specified.  Rules file ignored\n");
9347d5e3a19Smrg    }
9357d5e3a19Smrg    return True;
9367d5e3a19Smrg}
9377d5e3a19Smrg
9387d5e3a19Smrg/* Primitive sanity check - filter out 'map names' (inside parenthesis) */
9397d5e3a19Smrg/* that can confuse xkbcomp parser */
9401568b75bSmrgstatic Bool
941bda5b58fSmrgcheckName(char *name, const char *string)
9427d5e3a19Smrg{
9431568b75bSmrg    char *i = name, *opar = NULL;
9441568b75bSmrg    Bool ret = True;
9451568b75bSmrg
9461568b75bSmrg    if (!name)
9471568b75bSmrg        return True;
9481568b75bSmrg
9491568b75bSmrg    while (*i)
9501568b75bSmrg    {
9511568b75bSmrg        if (opar == NULL)
9521568b75bSmrg        {
9531568b75bSmrg            if (*i == '(')
9541568b75bSmrg                opar = i;
9551568b75bSmrg        }
9561568b75bSmrg        else
9571568b75bSmrg        {
9581568b75bSmrg            if ((*i == '(') || (*i == '|') || (*i == '+'))
9591568b75bSmrg            {
9601568b75bSmrg                ret = False;
9611568b75bSmrg                break;
9621568b75bSmrg            }
9631568b75bSmrg            if (*i == ')')
9641568b75bSmrg                opar = NULL;
9651568b75bSmrg        }
9661568b75bSmrg        i++;
9671568b75bSmrg    }
9681568b75bSmrg    if (opar)
9691568b75bSmrg        ret = False;
9701568b75bSmrg    if (!ret)
9711568b75bSmrg    {
9721568b75bSmrg        char c;
9731568b75bSmrg        int n = 1;
9741568b75bSmrg        for (i = opar + 1; *i && n; i++)
9751568b75bSmrg        {
9761568b75bSmrg            if (*i == '(')
9771568b75bSmrg                n++;
9781568b75bSmrg            if (*i == ')')
9791568b75bSmrg                n--;
9801568b75bSmrg        }
9811568b75bSmrg        if (*i)
9821568b75bSmrg            i++;
9831568b75bSmrg        c = *i;
9841568b75bSmrg        *i = '\0';
9851568b75bSmrg        ERR1("Illegal map name '%s' ", opar);
9861568b75bSmrg        *i = c;
9871568b75bSmrg        ERR2("in %s name '%s'\n", string, name);
9881568b75bSmrg    }
9891568b75bSmrg    return ret;
9907d5e3a19Smrg}
9917d5e3a19Smrg
9925212f86fSmrgstatic void
9937d5e3a19SmrgprintKeymap(void)
9947d5e3a19Smrg{
9957d5e3a19Smrg    MSG("xkb_keymap {\n");
996bda5b58fSmrg    if (settings.keycodes.value)
997bda5b58fSmrg        MSG1("\txkb_keycodes  { include \"%s\"\t};\n", settings.keycodes.value);
998bda5b58fSmrg    if (settings.types.value)
999bda5b58fSmrg        MSG1("\txkb_types     { include \"%s\"\t};\n", settings.types.value);
1000bda5b58fSmrg    if (settings.compat.value)
1001bda5b58fSmrg        MSG1("\txkb_compat    { include \"%s\"\t};\n", settings.compat.value);
1002bda5b58fSmrg    if (settings.symbols.value)
1003bda5b58fSmrg        MSG1("\txkb_symbols   { include \"%s\"\t};\n", settings.symbols.value);
1004bda5b58fSmrg    if (settings.geometry.value)
1005bda5b58fSmrg        MSG1("\txkb_geometry  { include \"%s\"\t};\n", settings.geometry.value);
10067d5e3a19Smrg    MSG("};\n");
10077d5e3a19Smrg}
10087d5e3a19Smrg
10095212f86fSmrgstatic Bool
10107d5e3a19SmrgapplyComponentNames(void)
10117d5e3a19Smrg{
1012bda5b58fSmrg    if (!checkName(settings.types.value, "types"))
10131568b75bSmrg        return False;
1014bda5b58fSmrg    if (!checkName(settings.compat.value, "compat"))
10151568b75bSmrg        return False;
1016bda5b58fSmrg    if (!checkName(settings.symbols.value, "symbols"))
10171568b75bSmrg        return False;
1018bda5b58fSmrg    if (!checkName(settings.keycodes.value, "keycodes"))
10191568b75bSmrg        return False;
1020bda5b58fSmrg    if (!checkName(settings.geometry.value, "geometry"))
10211568b75bSmrg        return False;
1022bda5b58fSmrg    if (!checkName(settings.keymap.value, "keymap"))
10231568b75bSmrg        return False;
10241568b75bSmrg
10251568b75bSmrg    if (verbose > 5)
10261568b75bSmrg    {
10271568b75bSmrg        MSG("Trying to build keymap using the following components:\n");
10281568b75bSmrg        dumpNames(False, True);
10291568b75bSmrg    }
10301568b75bSmrg    /* Upload the new description to the server. */
1031765486e8Smrg    if (dpy && !print && !query)
10321568b75bSmrg    {
1033b8414663Smrg        XkbComponentNamesRec cmdNames = {
1034b8414663Smrg            .keymap = settings.keymap.value,
1035b8414663Smrg            .keycodes = settings.keycodes.value,
1036b8414663Smrg            .types = settings.types.value,
1037b8414663Smrg            .compat = settings.compat.value,
1038b8414663Smrg            .symbols = settings.symbols.value,
1039b8414663Smrg            .geometry = settings.geometry.value
1040b8414663Smrg        };
1041b8414663Smrg
10421568b75bSmrg        xkb = XkbGetKeyboardByName(dpy, deviceSpec, &cmdNames,
10431568b75bSmrg                                   XkbGBN_AllComponentsMask,
10441568b75bSmrg                                   XkbGBN_AllComponentsMask &
10451568b75bSmrg                                   (~XkbGBN_GeometryMask), True);
10461568b75bSmrg        if (!xkb)
10471568b75bSmrg        {
10481568b75bSmrg            ERR("Error loading new keyboard description\n");
10491568b75bSmrg            return False;
10501568b75bSmrg        }
10511568b75bSmrg        /* update the XKB root property */
1052bda5b58fSmrg        if (settings.rules.value && (rdefs.model || rdefs.layout))
10531568b75bSmrg        {
1054bda5b58fSmrg            if (!XkbRF_SetNamesProp(dpy, settings.rules.value, &rdefs))
10551568b75bSmrg            {
10561568b75bSmrg                VMSG(0, "Error updating the XKB names property\n");
10571568b75bSmrg            }
10581568b75bSmrg        }
10591568b75bSmrg    }
10601568b75bSmrg    if (print)
10611568b75bSmrg    {
10627d5e3a19Smrg        printKeymap();
10637d5e3a19Smrg    }
1064765486e8Smrg    if (query)
1065765486e8Smrg    {
1066bda5b58fSmrg        dumpNames(True, False);
1067765486e8Smrg    }
10687d5e3a19Smrg    return True;
10697d5e3a19Smrg}
10707d5e3a19Smrg
10715212f86fSmrgstatic Bool
10725212f86fSmrgis_xwayland(void)
10735212f86fSmrg{
10745212f86fSmrg    /* Code copied from xisxwayland.c */
10755212f86fSmrg    Bool rc = False;
10765212f86fSmrg    XRRScreenResources *resources = NULL;
10775212f86fSmrg    XRROutputInfo *output = NULL;
10787f6d8fe2Smrg    int opcode, event_base, error_base, major, minor;
10795212f86fSmrg
10807f6d8fe2Smrg    /* With Xwayland 23.1 and above, the definitive way of checking for an
10817f6d8fe2Smrg     * Xwayland server is to check for the "XWAYLAND" extension.
10827f6d8fe2Smrg     */
10837f6d8fe2Smrg    if (XQueryExtension(dpy, "XWAYLAND", &opcode, &event_base, &error_base)) {
10847f6d8fe2Smrg        rc = True;
10857f6d8fe2Smrg        goto out;
10867f6d8fe2Smrg    }
10877f6d8fe2Smrg
10887f6d8fe2Smrg    /* For previous versions of Xwayland, there is no definitive way of checking
10897f6d8fe2Smrg     * for an Xwayland server, but the two working methods are:
10905212f86fSmrg     * - RandR output names in Xwayland are XWAYLAND0, XWAYLAND1, etc.
10915212f86fSmrg     * - XI devices are xwayland-pointer:10, xwayland-keyboard:11
10925212f86fSmrg     * Let's go with the XRandR check here because it's slightly less
10935212f86fSmrg     * code to write.
10945212f86fSmrg     */
10955212f86fSmrg
10965212f86fSmrg    if (!XRRQueryExtension(dpy, &event_base, &error_base) ||
10975212f86fSmrg            !XRRQueryVersion(dpy, &major, &minor)) {
10985212f86fSmrg        /* e.g. Xnest, but definitely not Xwayland */
10995212f86fSmrg        goto out;
11005212f86fSmrg    }
11017f6d8fe2Smrg    if (major < 1 || (major == 1 && minor < 3)) {
11027f6d8fe2Smrg        goto out;
11037f6d8fe2Smrg    }
11045212f86fSmrg
11055212f86fSmrg    resources = XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy));
11067f6d8fe2Smrg    if (!resources || resources->noutput < 1) {
11075212f86fSmrg        goto out;
11085212f86fSmrg    }
11095212f86fSmrg
11105212f86fSmrg    output = XRRGetOutputInfo(dpy, resources, resources->outputs[0]);
11115212f86fSmrg    if (!output) {
11125212f86fSmrg        goto out;
11135212f86fSmrg    }
11145212f86fSmrg
11155212f86fSmrg    if (strncmp(output->name, "XWAYLAND", 8) == 0)
11165212f86fSmrg        rc = True;
11175212f86fSmrg
11185212f86fSmrg    XRRFreeOutputInfo(output);
11195212f86fSmrgout:
11205212f86fSmrg    if (resources)
11215212f86fSmrg        XRRFreeScreenResources(resources);
11225212f86fSmrg
11235212f86fSmrg    return rc;
11245212f86fSmrg}
11257d5e3a19Smrg
11267d5e3a19Smrgint
11271568b75bSmrgmain(int argc, char **argv)
11287d5e3a19Smrg{
11291568b75bSmrg    if ((!parseArgs(argc, argv)) || (!getDisplay(argc, argv)))
11305212f86fSmrg        exit(1);
11315212f86fSmrg
11325212f86fSmrg    if (is_xwayland())
11337f6d8fe2Smrg	    ERR("WARNING: Running setxkbmap against an Xwayland server\n");
11345212f86fSmrg
1135bda5b58fSmrg    settings.locale.value = setlocale(LC_ALL, settings.locale.value);
1136bda5b58fSmrg    settings.locale.src = FROM_SERVER;
1137bda5b58fSmrg    VMSG1(7, "locale is %s\n", settings.locale.value);
11387d5e3a19Smrg    if (dpy)
11397d5e3a19Smrg        getServerValues();
1140bda5b58fSmrg    if (settings.config.value && (!applyConfig(settings.config.value)))
11415212f86fSmrg        exit(3);
11427d5e3a19Smrg    if (!applyRules())
11435212f86fSmrg        exit(4);
11447d5e3a19Smrg    if (!applyComponentNames())
11455212f86fSmrg        exit(5);
11467d5e3a19Smrg    if (dpy)
11471568b75bSmrg        XCloseDisplay(dpy);
11487d5e3a19Smrg    exit(0);
11497d5e3a19Smrg}
1150