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