Home | History | Annotate | Line # | Download | only in src
      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_DIX_CONFIG_H
     28 #include <dix-config.h>
     29 #elif defined(HAVE_CONFIG_H)
     30 #include <config.h>
     31 #endif
     32 
     33 #include <stdio.h>
     34 #include <ctype.h>
     35 #include <stdlib.h>
     36 
     37 #define X_INCLUDE_STRING_H
     38 #define XOS_USE_NO_LOCKING
     39 #include <X11/Xos_r.h>
     40 
     41 
     42 #include <X11/Xproto.h>
     43 #include <X11/Xlib.h>
     44 #include <X11/Xos.h>
     45 #include <X11/Xfuncs.h>
     46 #include <X11/Xatom.h>
     47 #include <X11/keysym.h>
     48 #include <X11/XKBlib.h>
     49 #include <X11/extensions/XKBgeom.h>
     50 #include "XKMformat.h"
     51 #include "XKBfileInt.h"
     52 #include "XKBrules.h"
     53 
     54 #ifdef O_CLOEXEC
     55 #define FOPEN_CLOEXEC "e"
     56 #else
     57 #define FOPEN_CLOEXEC ""
     58 #endif
     59 
     60 #ifdef DEBUG
     61 #define PR_DEBUG(s)		fprintf(stderr,s)
     62 #define PR_DEBUG1(s,a)		fprintf(stderr,s,a)
     63 #define PR_DEBUG2(s,a,b)	fprintf(stderr,s,a,b)
     64 #else
     65 #define PR_DEBUG(s)
     66 #define PR_DEBUG1(s,a)
     67 #define PR_DEBUG2(s,a,b)
     68 #endif
     69 
     70 /***====================================================================***/
     71 
     72 #define DFLT_LINE_SIZE	128
     73 
     74 typedef struct {
     75     int         line_num;
     76     int         sz_line;
     77     int         num_line;
     78     char        buf[DFLT_LINE_SIZE];
     79     char *      line;
     80 } InputLine;
     81 
     82 static void
     83 InitInputLine(InputLine *line)
     84 {
     85     line->line_num = 1;
     86     line->num_line = 0;
     87     line->sz_line = DFLT_LINE_SIZE;
     88     line->line = line->buf;
     89     return;
     90 }
     91 
     92 static void
     93 FreeInputLine(InputLine *line)
     94 {
     95     if (line->line != line->buf)
     96         _XkbFree(line->line);
     97     line->line_num = 1;
     98     line->num_line = 0;
     99     line->sz_line = DFLT_LINE_SIZE;
    100     line->line = line->buf;
    101     return;
    102 }
    103 
    104 static int
    105 InputLineAddChar(InputLine *line, int ch)
    106 {
    107     if (line->num_line >= line->sz_line) {
    108         if (line->line == line->buf) {
    109             line->line = _XkbAlloc(line->sz_line * 2);
    110             memcpy(line->line, line->buf, line->sz_line);
    111         }
    112         else {
    113             line->line = _XkbRealloc(line->line, line->sz_line * 2);
    114         }
    115         line->sz_line *= 2;
    116     }
    117     line->line[line->num_line++] = ch;
    118     return ch;
    119 }
    120 
    121 #define	ADD_CHAR(l,c)	((l)->num_line<(l)->sz_line?\
    122 				(int)((l)->line[(l)->num_line++]= (c)):\
    123 				InputLineAddChar(l,c))
    124 
    125 #ifdef HAVE_UNLOCKED_STDIO
    126 #undef getc
    127 #define getc(x) getc_unlocked(x)
    128 #else
    129 #define flockfile(x) do {} while (0)
    130 #define funlockfile(x) do {} while (0)
    131 #endif
    132 
    133 static Bool
    134 GetInputLine(FILE *file, InputLine *line, Bool checkbang)
    135 {
    136     int ch;
    137     Bool endOfFile, spacePending, slashPending, inComment;
    138 
    139     endOfFile = False;
    140     flockfile(file);
    141     while ((!endOfFile) && (line->num_line == 0)) {
    142         spacePending = slashPending = inComment = False;
    143         while (((ch = getc(file)) != '\n') && (ch != EOF)) {
    144             if (ch == '\\') {
    145                 if ((ch = getc(file)) == EOF)
    146                     break;
    147                 if (ch == '\n') {
    148                     inComment = False;
    149                     ch = ' ';
    150                     line->line_num++;
    151                 }
    152             }
    153             if (inComment)
    154                 continue;
    155             if (ch == '/') {
    156                 if (slashPending) {
    157                     inComment = True;
    158                     slashPending = False;
    159                 }
    160                 else {
    161                     slashPending = True;
    162                 }
    163                 continue;
    164             }
    165             else if (slashPending) {
    166                 if (spacePending) {
    167                     ADD_CHAR(line, ' ');
    168                     spacePending = False;
    169                 }
    170                 ADD_CHAR(line, '/');
    171                 slashPending = False;
    172             }
    173             if (isspace(ch)) {
    174                 while (isspace(ch) && (ch != '\n') && (ch != EOF)) {
    175                     ch = getc(file);
    176                 }
    177                 if (ch == EOF)
    178                     break;
    179                 if ((ch != '\n') && (line->num_line > 0))
    180                     spacePending = True;
    181                 ungetc(ch, file);
    182             }
    183             else {
    184                 if (spacePending) {
    185                     ADD_CHAR(line, ' ');
    186                     spacePending = False;
    187                 }
    188                 if (checkbang && ch == '!') {
    189                     if (line->num_line != 0) {
    190                         PR_DEBUG("The '!' legal only at start of line\n");
    191                         PR_DEBUG("Line containing '!' ignored\n");
    192                         line->num_line = 0;
    193                         inComment = 0;
    194                         break;
    195                     }
    196 
    197                 }
    198                 ADD_CHAR(line, ch);
    199             }
    200         }
    201         if (ch == EOF)
    202             endOfFile = True;
    203 /*	else line->num_line++;*/
    204     }
    205     funlockfile(file);
    206     if ((line->num_line == 0) && (endOfFile))
    207         return False;
    208     ADD_CHAR(line, '\0');
    209     return True;
    210 }
    211 
    212 /***====================================================================***/
    213 
    214 #define	MODEL		0
    215 #define	LAYOUT		1
    216 #define	VARIANT		2
    217 #define	OPTION		3
    218 #define	KEYCODES	4
    219 #define SYMBOLS		5
    220 #define	TYPES		6
    221 #define	COMPAT		7
    222 #define	GEOMETRY	8
    223 #define	KEYMAP		9
    224 #define	MAX_WORDS	10
    225 
    226 #define	PART_MASK	0x000F
    227 #define	COMPONENT_MASK	0x03F0
    228 
    229 static const char *cname[MAX_WORDS] = {
    230     "model", "layout", "variant", "option",
    231     "keycodes", "symbols", "types", "compat", "geometry", "keymap"
    232 };
    233 
    234 typedef struct _RemapSpec {
    235     int number;
    236     int num_remap;
    237     struct {
    238         int word;
    239         int index;
    240     } remap[MAX_WORDS];
    241 } RemapSpec;
    242 
    243 typedef struct _FileSpec {
    244     char *name[MAX_WORDS];
    245     struct _FileSpec *pending;
    246 } FileSpec;
    247 
    248 typedef struct {
    249     char *model;
    250     char *layout[XkbNumKbdGroups + 1];
    251     char *variant[XkbNumKbdGroups + 1];
    252     char *options;
    253 } XkbRF_MultiDefsRec, *XkbRF_MultiDefsPtr;
    254 
    255 #define NDX_BUFF_SIZE	4
    256 
    257 /***====================================================================***/
    258 
    259 static char *
    260 get_index(char *str, int *ndx)
    261 {
    262     char ndx_buf[NDX_BUFF_SIZE];
    263 
    264     char *end;
    265 
    266     if (*str != '[') {
    267         *ndx = 0;
    268         return str;
    269     }
    270     str++;
    271     end = strchr(str, ']');
    272     if (end == NULL) {
    273         *ndx = -1;
    274         return str - 1;
    275     }
    276     if ((end - str) >= NDX_BUFF_SIZE) {
    277         *ndx = -1;
    278         return end + 1;
    279     }
    280     strncpy(ndx_buf, str, end - str);
    281     ndx_buf[end - str] = '\0';
    282     *ndx = atoi(ndx_buf);
    283     return end + 1;
    284 }
    285 
    286 static void
    287 SetUpRemap(InputLine *line, RemapSpec *remap)
    288 {
    289     char *tok, *str;
    290     unsigned present, l_ndx_present, v_ndx_present;
    291     register int i;
    292     int len, ndx;
    293     _Xstrtokparams strtok_buf;
    294 
    295 #ifdef DEBUG
    296     Bool found;
    297 #endif
    298 
    299     l_ndx_present = v_ndx_present = present = 0;
    300     str = &line->line[1];
    301     len = remap->number;
    302     memset(remap, 0, sizeof(RemapSpec));
    303     remap->number = len;
    304     while ((tok = _XStrtok(str, " ", strtok_buf)) != NULL) {
    305 #ifdef DEBUG
    306         found = False;
    307 #endif
    308         str = NULL;
    309         if (strcmp(tok, "=") == 0)
    310             continue;
    311         for (i = 0; i < MAX_WORDS; i++) {
    312             len = strlen(cname[i]);
    313             if (strncmp(cname[i], tok, len) == 0) {
    314                 if (strlen(tok) > len) {
    315                     char *end = get_index(tok + len, &ndx);
    316 
    317                     if ((i != LAYOUT && i != VARIANT) ||
    318                         *end != '\0' || ndx == -1)
    319                         break;
    320                     if (ndx < 1 || ndx > XkbNumKbdGroups) {
    321                         PR_DEBUG2("Illegal %s index: %d\n", cname[i], ndx);
    322                         PR_DEBUG1("Index must be in range 1..%d\n",
    323                                   XkbNumKbdGroups);
    324                         break;
    325                     }
    326                 }
    327                 else {
    328                     ndx = 0;
    329                 }
    330 #ifdef DEBUG
    331                 found = True;
    332 #endif
    333                 if (present & (1 << i)) {
    334                     if ((i == LAYOUT && l_ndx_present & (1 << ndx)) ||
    335                         (i == VARIANT && v_ndx_present & (1 << ndx))) {
    336                         PR_DEBUG1("Component \"%s\" listed twice\n", tok);
    337                         PR_DEBUG("Second definition ignored\n");
    338                         break;
    339                     }
    340                 }
    341                 present |= (1 << i);
    342                 if (i == LAYOUT)
    343                     l_ndx_present |= 1 << ndx;
    344                 if (i == VARIANT)
    345                     v_ndx_present |= 1 << ndx;
    346                 remap->remap[remap->num_remap].word = i;
    347                 remap->remap[remap->num_remap++].index = ndx;
    348                 break;
    349             }
    350         }
    351 #ifdef DEBUG
    352         if (!found) {
    353             fprintf(stderr, "Unknown component \"%s\" ignored\n", tok);
    354         }
    355 #endif
    356     }
    357     if ((present & PART_MASK) == 0) {
    358 #ifdef DEBUG
    359         unsigned mask = PART_MASK;
    360 
    361         fprintf(stderr, "Mapping needs at least one of ");
    362         for (i = 0; (i < MAX_WORDS); i++) {
    363             if ((1L << i) & mask) {
    364                 mask &= ~(1L << i);
    365                 if (mask)
    366                     fprintf(stderr, "\"%s,\" ", cname[i]);
    367                 else
    368                     fprintf(stderr, "or \"%s\"\n", cname[i]);
    369             }
    370         }
    371         fprintf(stderr, "Illegal mapping ignored\n");
    372 #endif
    373         remap->num_remap = 0;
    374         return;
    375     }
    376     if ((present & COMPONENT_MASK) == 0) {
    377         PR_DEBUG("Mapping needs at least one component\n");
    378         PR_DEBUG("Illegal mapping ignored\n");
    379         remap->num_remap = 0;
    380         return;
    381     }
    382     if (((present & COMPONENT_MASK) & (1 << KEYMAP)) &&
    383         ((present & COMPONENT_MASK) != (1 << KEYMAP))) {
    384         PR_DEBUG("Keymap cannot appear with other components\n");
    385         PR_DEBUG("Illegal mapping ignored\n");
    386         remap->num_remap = 0;
    387         return;
    388     }
    389     remap->number++;
    390     return;
    391 }
    392 
    393 static Bool
    394 MatchOneOf(char *wanted, char *vals_defined)
    395 {
    396     char *str, *next;
    397     int want_len = strlen(wanted);
    398 
    399     for (str = vals_defined, next = NULL; str != NULL; str = next) {
    400         int len;
    401 
    402         next = strchr(str, ',');
    403         if (next) {
    404             len = next - str;
    405             next++;
    406         }
    407         else {
    408             len = strlen(str);
    409         }
    410         if ((len == want_len) && (strncmp(wanted, str, len) == 0))
    411             return True;
    412     }
    413     return False;
    414 }
    415 
    416 /***====================================================================***/
    417 
    418 static Bool
    419 CheckLine(InputLine *    line,
    420           RemapSpec *    remap,
    421           XkbRF_RulePtr  rule,
    422           XkbRF_GroupPtr group)
    423 {
    424     char *str, *tok;
    425     register int nread, i;
    426     FileSpec tmp;
    427     _Xstrtokparams strtok_buf;
    428     Bool append = False;
    429 
    430     if (line->line[0] == '!') {
    431         if (line->line[1] == '$' ||
    432             (line->line[1] == ' ' && line->line[2] == '$')) {
    433             char *gname = strchr(line->line, '$');
    434             char *words = strchr(gname, ' ');
    435 
    436             if (!words)
    437                 return False;
    438             *words++ = '\0';
    439             for (; *words; words++) {
    440                 if (*words != '=' && *words != ' ')
    441                     break;
    442             }
    443             if (*words == '\0')
    444                 return False;
    445             group->name = _XkbDupString(gname);
    446             group->words = _XkbDupString(words);
    447             for (i = 1, words = group->words; *words; words++) {
    448                 if (*words == ' ') {
    449                     *words++ = '\0';
    450                     i++;
    451                 }
    452             }
    453             group->number = i;
    454             return True;
    455         }
    456         else {
    457             SetUpRemap(line, remap);
    458             return False;
    459         }
    460     }
    461 
    462     if (remap->num_remap == 0) {
    463         PR_DEBUG("Must have a mapping before first line of data\n");
    464         PR_DEBUG("Illegal line of data ignored\n");
    465         return False;
    466     }
    467     memset(&tmp, 0, sizeof(FileSpec));
    468     str = line->line;
    469     for (nread = 0; (tok = _XStrtok(str, " ", strtok_buf)) != NULL; nread++) {
    470         str = NULL;
    471         if (strcmp(tok, "=") == 0) {
    472             nread--;
    473             continue;
    474         }
    475         if (nread > remap->num_remap) {
    476             PR_DEBUG("Too many words on a line\n");
    477             PR_DEBUG1("Extra word \"%s\" ignored\n", tok);
    478             continue;
    479         }
    480         tmp.name[remap->remap[nread].word] = tok;
    481         if (*tok == '+' || *tok == '|')
    482             append = True;
    483     }
    484     if (nread < remap->num_remap) {
    485         PR_DEBUG1("Too few words on a line: %s\n", line->line);
    486         PR_DEBUG("line ignored\n");
    487         return False;
    488     }
    489 
    490     rule->flags = 0;
    491     rule->number = remap->number;
    492     if (tmp.name[OPTION])
    493         rule->flags |= XkbRF_Option;
    494     else if (append)
    495         rule->flags |= XkbRF_Append;
    496     else
    497         rule->flags |= XkbRF_Normal;
    498     rule->model = _XkbDupString(tmp.name[MODEL]);
    499     rule->layout = _XkbDupString(tmp.name[LAYOUT]);
    500     rule->variant = _XkbDupString(tmp.name[VARIANT]);
    501     rule->option = _XkbDupString(tmp.name[OPTION]);
    502 
    503     rule->keycodes = _XkbDupString(tmp.name[KEYCODES]);
    504     rule->symbols = _XkbDupString(tmp.name[SYMBOLS]);
    505     rule->types = _XkbDupString(tmp.name[TYPES]);
    506     rule->compat = _XkbDupString(tmp.name[COMPAT]);
    507     rule->geometry = _XkbDupString(tmp.name[GEOMETRY]);
    508     rule->keymap = _XkbDupString(tmp.name[KEYMAP]);
    509 
    510     rule->layout_num = rule->variant_num = 0;
    511     for (i = 0; i < nread; i++) {
    512         if (remap->remap[i].index) {
    513             if (remap->remap[i].word == LAYOUT)
    514                 rule->layout_num = remap->remap[i].index;
    515             if (remap->remap[i].word == VARIANT)
    516                 rule->variant_num = remap->remap[i].index;
    517         }
    518     }
    519     return True;
    520 }
    521 
    522 static char *
    523 _Concat(char *str1, char *str2)
    524 {
    525     int len;
    526 
    527     if ((!str1) || (!str2))
    528         return str1;
    529     len = strlen(str1) + strlen(str2) + 1;
    530     str1 = _XkbTypedReallocF(str1, len, char);
    531     if (str1)
    532         strcat(str1, str2);
    533     return str1;
    534 }
    535 
    536 static void
    537 squeeze_spaces(char *p1)
    538 {
    539     char *p2;
    540 
    541     for (p2 = p1; *p2; p2++) {
    542         *p1 = *p2;
    543         if (*p1 != ' ')
    544             p1++;
    545     }
    546     *p1 = '\0';
    547 }
    548 
    549 static Bool
    550 MakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs)
    551 {
    552 
    553     memset(mdefs, 0, sizeof(XkbRF_MultiDefsRec));
    554     mdefs->model = defs->model;
    555     mdefs->options = _XkbDupString(defs->options);
    556     if (mdefs->options)
    557         squeeze_spaces(mdefs->options);
    558 
    559     if (defs->layout) {
    560         if (!strchr(defs->layout, ',')) {
    561             mdefs->layout[0] = defs->layout;
    562         }
    563         else {
    564             char *p;
    565 
    566             int i;
    567 
    568             mdefs->layout[1] = _XkbDupString(defs->layout);
    569             if (mdefs->layout[1] == NULL)
    570                 return False;
    571             squeeze_spaces(mdefs->layout[1]);
    572             p = mdefs->layout[1];
    573             for (i = 2; i <= XkbNumKbdGroups; i++) {
    574                 if ((p = strchr(p, ','))) {
    575                     *p++ = '\0';
    576                     mdefs->layout[i] = p;
    577                 }
    578                 else {
    579                     break;
    580                 }
    581             }
    582             if (p && (p = strchr(p, ',')))
    583                 *p = '\0';
    584         }
    585     }
    586 
    587     if (defs->variant) {
    588         if (!strchr(defs->variant, ',')) {
    589             mdefs->variant[0] = defs->variant;
    590         }
    591         else {
    592             char *p;
    593 
    594             int i;
    595 
    596             mdefs->variant[1] = _XkbDupString(defs->variant);
    597             if (mdefs->variant[1] == NULL)
    598                 return False;
    599             squeeze_spaces(mdefs->variant[1]);
    600             p = mdefs->variant[1];
    601             for (i = 2; i <= XkbNumKbdGroups; i++) {
    602                 if ((p = strchr(p, ','))) {
    603                     *p++ = '\0';
    604                     mdefs->variant[i] = p;
    605                 }
    606                 else {
    607                     break;
    608                 }
    609             }
    610             if (p && (p = strchr(p, ',')))
    611                 *p = '\0';
    612         }
    613     }
    614     return True;
    615 }
    616 
    617 static void
    618 FreeMultiDefs(XkbRF_MultiDefsPtr defs)
    619 {
    620     if (defs->options)
    621         _XkbFree(defs->options);
    622     if (defs->layout[1])
    623         _XkbFree(defs->layout[1]);
    624     if (defs->variant[1])
    625         _XkbFree(defs->variant[1]);
    626 }
    627 
    628 static void
    629 Apply(char *src, char **dst)
    630 {
    631     if (src) {
    632         if (*src == '+' || *src == '|') {
    633             *dst = _Concat(*dst, src);
    634         }
    635         else {
    636             if (*dst == NULL)
    637                 *dst = _XkbDupString(src);
    638         }
    639     }
    640 }
    641 
    642 static void
    643 XkbRF_ApplyRule(XkbRF_RulePtr rule, XkbComponentNamesPtr names)
    644 {
    645     rule->flags &= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
    646 
    647     Apply(rule->keycodes, &names->keycodes);
    648     Apply(rule->symbols,  &names->symbols);
    649     Apply(rule->types,    &names->types);
    650     Apply(rule->compat,   &names->compat);
    651     Apply(rule->geometry, &names->geometry);
    652     Apply(rule->keymap,   &names->keymap);
    653 }
    654 
    655 static Bool
    656 CheckGroup(XkbRF_RulesPtr rules, char *group_name, char *name)
    657 {
    658     int i;
    659     char *p;
    660     XkbRF_GroupPtr group;
    661 
    662     for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
    663         if (!strcmp(group->name, group_name)) {
    664             break;
    665         }
    666     }
    667     if (i == rules->num_groups)
    668         return False;
    669     for (i = 0, p = group->words; i < group->number; i++, p += strlen(p) + 1) {
    670         if (!strcmp(p, name)) {
    671             return True;
    672         }
    673     }
    674     return False;
    675 }
    676 
    677 static int
    678 XkbRF_CheckApplyRule(XkbRF_RulePtr        rule,
    679                      XkbRF_MultiDefsPtr   mdefs,
    680                      XkbComponentNamesPtr names,
    681                      XkbRF_RulesPtr       rules)
    682 {
    683     Bool pending = False;
    684 
    685     if (rule->model != NULL) {
    686         if (mdefs->model == NULL)
    687             return 0;
    688         if (strcmp(rule->model, "*") == 0) {
    689             pending = True;
    690         }
    691         else {
    692             if (rule->model[0] == '$') {
    693                 if (!CheckGroup(rules, rule->model, mdefs->model))
    694                     return 0;
    695             }
    696             else {
    697                 if (strcmp(rule->model, mdefs->model) != 0)
    698                     return 0;
    699             }
    700         }
    701     }
    702     if (rule->option != NULL) {
    703         if (mdefs->options == NULL)
    704             return 0;
    705         if ((!MatchOneOf(rule->option, mdefs->options)))
    706             return 0;
    707     }
    708 
    709     if (rule->layout != NULL) {
    710         if (mdefs->layout[rule->layout_num] == NULL ||
    711             *mdefs->layout[rule->layout_num] == '\0')
    712             return 0;
    713         if (strcmp(rule->layout, "*") == 0) {
    714             pending = True;
    715         }
    716         else {
    717             if (rule->layout[0] == '$') {
    718                 if (!CheckGroup(rules, rule->layout,
    719                                 mdefs->layout[rule->layout_num]))
    720                     return 0;
    721             }
    722             else {
    723                 if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
    724                     return 0;
    725             }
    726         }
    727     }
    728     if (rule->variant != NULL) {
    729         if (mdefs->variant[rule->variant_num] == NULL ||
    730             *mdefs->variant[rule->variant_num] == '\0')
    731             return 0;
    732         if (strcmp(rule->variant, "*") == 0) {
    733             pending = True;
    734         }
    735         else {
    736             if (rule->variant[0] == '$') {
    737                 if (!CheckGroup(rules, rule->variant,
    738                                 mdefs->variant[rule->variant_num]))
    739                     return 0;
    740             }
    741             else {
    742                 if (strcmp(rule->variant,
    743                            mdefs->variant[rule->variant_num]) != 0)
    744                     return 0;
    745             }
    746         }
    747     }
    748     if (pending) {
    749         rule->flags |= XkbRF_PendingMatch;
    750         return rule->number;
    751     }
    752     /* exact match, apply it now */
    753     XkbRF_ApplyRule(rule, names);
    754     return rule->number;
    755 }
    756 
    757 static void
    758 XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
    759 {
    760     register int i;
    761     XkbRF_RulePtr rule;
    762 
    763     for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) {
    764         rule->flags &= ~XkbRF_PendingMatch;
    765     }
    766 }
    767 
    768 static void
    769 XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules, XkbComponentNamesPtr names)
    770 {
    771     int i;
    772     XkbRF_RulePtr rule;
    773 
    774     for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
    775         if ((rule->flags & XkbRF_PendingMatch) == 0)
    776             continue;
    777         XkbRF_ApplyRule(rule, names);
    778     }
    779 }
    780 
    781 static void
    782 XkbRF_CheckApplyRules(XkbRF_RulesPtr       rules,
    783                       XkbRF_MultiDefsPtr   mdefs,
    784                       XkbComponentNamesPtr names,
    785                       int                  flags)
    786 {
    787     int i;
    788     XkbRF_RulePtr rule;
    789     int skip;
    790 
    791     for (rule = rules->rules, i = 0; i < rules->num_rules; rule++, i++) {
    792         if ((rule->flags & flags) != flags)
    793             continue;
    794         skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
    795         if (skip && !(flags & XkbRF_Option)) {
    796             for (; (i < rules->num_rules) && (rule->number == skip);
    797                  rule++, i++);
    798             rule--;
    799             i--;
    800         }
    801     }
    802 }
    803 
    804 /***====================================================================***/
    805 
    806 static char *
    807 XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
    808 {
    809     char *str, *outstr, *orig, *var;
    810     int len, ndx;
    811 
    812     orig = name;
    813     str = index(name, '%');
    814     if (str == NULL)
    815         return name;
    816     len = strlen(name);
    817     while (str != NULL) {
    818         char pfx = str[1];
    819         int extra_len = 0;
    820 
    821         if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) {
    822             extra_len = 1;
    823             str++;
    824         }
    825         else if (pfx == '(') {
    826             extra_len = 2;
    827             str++;
    828         }
    829         var = str + 1;
    830         str = get_index(var + 1, &ndx);
    831         if (ndx == -1) {
    832             str = index(str, '%');
    833             continue;
    834         }
    835         if ((*var == 'l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
    836             len += strlen(mdefs->layout[ndx]) + extra_len;
    837         else if ((*var == 'm') && mdefs->model)
    838             len += strlen(mdefs->model) + extra_len;
    839         else if ((*var == 'v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
    840             len += strlen(mdefs->variant[ndx]) + extra_len;
    841         if ((pfx == '(') && (*str == ')')) {
    842             str++;
    843         }
    844         str = index(&str[0], '%');
    845     }
    846     name = (char *) _XkbAlloc(len + 1);
    847     str = orig;
    848     outstr = name;
    849     while (*str != '\0') {
    850         if (str[0] == '%') {
    851             char pfx, sfx;
    852 
    853             str++;
    854             pfx = str[0];
    855             sfx = '\0';
    856             if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) {
    857                 str++;
    858             }
    859             else if (pfx == '(') {
    860                 sfx = ')';
    861                 str++;
    862             }
    863             else
    864                 pfx = '\0';
    865 
    866             var = str;
    867             str = get_index(var + 1, &ndx);
    868             if (ndx == -1) {
    869                 continue;
    870             }
    871             if ((*var == 'l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
    872                 if (pfx)
    873                     *outstr++ = pfx;
    874                 strcpy(outstr, mdefs->layout[ndx]);
    875                 outstr += strlen(mdefs->layout[ndx]);
    876                 if (sfx)
    877                     *outstr++ = sfx;
    878             }
    879             else if ((*var == 'm') && (mdefs->model)) {
    880                 if (pfx)
    881                     *outstr++ = pfx;
    882                 strcpy(outstr, mdefs->model);
    883                 outstr += strlen(mdefs->model);
    884                 if (sfx)
    885                     *outstr++ = sfx;
    886             }
    887             else if ((*var == 'v') && mdefs->variant[ndx] &&
    888                      *mdefs->variant[ndx]) {
    889                 if (pfx)
    890                     *outstr++ = pfx;
    891                 strcpy(outstr, mdefs->variant[ndx]);
    892                 outstr += strlen(mdefs->variant[ndx]);
    893                 if (sfx)
    894                     *outstr++ = sfx;
    895             }
    896             if ((pfx == '(') && (*str == ')'))
    897                 str++;
    898         }
    899         else {
    900             *outstr++ = *str++;
    901         }
    902     }
    903     *outstr++ = '\0';
    904     if (orig != name)
    905         _XkbFree(orig);
    906     return name;
    907 }
    908 
    909 /***====================================================================***/
    910 
    911 Bool
    912 XkbRF_GetComponents(XkbRF_RulesPtr       rules,
    913                     XkbRF_VarDefsPtr     defs,
    914                     XkbComponentNamesPtr names)
    915 {
    916     XkbRF_MultiDefsRec mdefs;
    917 
    918     MakeMultiDefs(&mdefs, defs);
    919 
    920     memset(names, 0, sizeof(XkbComponentNamesRec));
    921     XkbRF_ClearPartialMatches(rules);
    922     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
    923     XkbRF_ApplyPartialMatches(rules, names);
    924     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
    925     XkbRF_ApplyPartialMatches(rules, names);
    926     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
    927     XkbRF_ApplyPartialMatches(rules, names);
    928 
    929     if (names->keycodes)
    930         names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs);
    931     if (names->symbols)
    932         names->symbols  = XkbRF_SubstituteVars(names->symbols, &mdefs);
    933     if (names->types)
    934         names->types    = XkbRF_SubstituteVars(names->types, &mdefs);
    935     if (names->compat)
    936         names->compat   = XkbRF_SubstituteVars(names->compat, &mdefs);
    937     if (names->geometry)
    938         names->geometry = XkbRF_SubstituteVars(names->geometry, &mdefs);
    939     if (names->keymap)
    940         names->keymap   = XkbRF_SubstituteVars(names->keymap, &mdefs);
    941 
    942     FreeMultiDefs(&mdefs);
    943     return (names->keycodes && names->symbols && names->types &&
    944             names->compat && names->geometry) || names->keymap;
    945 }
    946 
    947 XkbRF_RulePtr
    948 XkbRF_AddRule(XkbRF_RulesPtr rules)
    949 {
    950     if (rules->sz_rules < 1) {
    951         rules->sz_rules = 16;
    952         rules->num_rules = 0;
    953         rules->rules = _XkbTypedCalloc(rules->sz_rules, XkbRF_RuleRec);
    954     }
    955     else if (rules->num_rules >= rules->sz_rules) {
    956         rules->sz_rules *= 2;
    957         rules->rules = _XkbTypedReallocF(rules->rules, rules->sz_rules,
    958                                          XkbRF_RuleRec);
    959     }
    960     if (!rules->rules) {
    961         rules->sz_rules = rules->num_rules = 0;
    962 #ifdef DEBUG
    963         fprintf(stderr, "Allocation failure in XkbRF_AddRule\n");
    964 #endif
    965         return NULL;
    966     }
    967     memset(&rules->rules[rules->num_rules], 0, sizeof(XkbRF_RuleRec));
    968     return &rules->rules[rules->num_rules++];
    969 }
    970 
    971 XkbRF_GroupPtr
    972 XkbRF_AddGroup(XkbRF_RulesPtr rules)
    973 {
    974     if (rules->sz_groups < 1) {
    975         rules->sz_groups = 16;
    976         rules->num_groups = 0;
    977         rules->groups = _XkbTypedCalloc(rules->sz_groups, XkbRF_GroupRec);
    978     }
    979     else if (rules->num_groups >= rules->sz_groups) {
    980         rules->sz_groups *= 2;
    981         rules->groups = _XkbTypedReallocF(rules->groups, rules->sz_groups,
    982                                           XkbRF_GroupRec);
    983     }
    984     if (!rules->groups) {
    985         rules->sz_groups = rules->num_groups = 0;
    986         return NULL;
    987     }
    988 
    989     memset(&rules->groups[rules->num_groups], 0, sizeof(XkbRF_GroupRec));
    990     return &rules->groups[rules->num_groups++];
    991 }
    992 
    993 Bool
    994 XkbRF_LoadRules(FILE *file, XkbRF_RulesPtr rules)
    995 {
    996     InputLine line;
    997     RemapSpec remap;
    998     XkbRF_RuleRec trule, *rule;
    999     XkbRF_GroupRec tgroup, *group;
   1000 
   1001     if (!(rules && file))
   1002         return False;
   1003     memset(&remap, 0, sizeof(RemapSpec));
   1004     memset(&tgroup, 0, sizeof(XkbRF_GroupRec));
   1005     InitInputLine(&line);
   1006     while (GetInputLine(file, &line, True)) {
   1007         if (CheckLine(&line, &remap, &trule, &tgroup)) {
   1008             if (tgroup.number) {
   1009                 if ((group = XkbRF_AddGroup(rules)) != NULL) {
   1010                     *group = tgroup;
   1011                     memset(&tgroup, 0, sizeof(XkbRF_GroupRec));
   1012                 }
   1013             }
   1014             else {
   1015                 if ((rule = XkbRF_AddRule(rules)) != NULL) {
   1016                     *rule = trule;
   1017                     memset(&trule, 0, sizeof(XkbRF_RuleRec));
   1018                 }
   1019             }
   1020         }
   1021         line.num_line = 0;
   1022     }
   1023     FreeInputLine(&line);
   1024     return True;
   1025 }
   1026 
   1027 Bool
   1028 XkbRF_LoadRulesByName(char *base, char *locale, XkbRF_RulesPtr rules)
   1029 {
   1030     FILE *file;
   1031     char buf[PATH_MAX];
   1032     Bool ok;
   1033 
   1034     if ((!base) || (!rules))
   1035         return False;
   1036     if (locale) {
   1037         if (snprintf(buf, sizeof(buf), "%s-%s", base, locale) >= (int) sizeof(buf))
   1038             return False;
   1039     }
   1040     else {
   1041         if (strlen(base) + 1 > PATH_MAX)
   1042             return False;
   1043         strcpy(buf, base);
   1044     }
   1045 
   1046     file = fopen(buf, "r" FOPEN_CLOEXEC);
   1047     if ((!file) && (locale)) {  /* fallback if locale was specified */
   1048         strcpy(buf, base);
   1049         file = fopen(buf, "r" FOPEN_CLOEXEC);
   1050     }
   1051     if (!file)
   1052         return False;
   1053     ok = XkbRF_LoadRules(file, rules);
   1054     fclose(file);
   1055     return ok;
   1056 }
   1057 
   1058 /***====================================================================***/
   1059 
   1060 #define HEAD_NONE	0
   1061 #define HEAD_MODEL	1
   1062 #define HEAD_LAYOUT	2
   1063 #define HEAD_VARIANT	3
   1064 #define HEAD_OPTION	4
   1065 #define	HEAD_EXTRA	5
   1066 
   1067 XkbRF_VarDescPtr
   1068 XkbRF_AddVarDesc(XkbRF_DescribeVarsPtr vars)
   1069 {
   1070     if (vars->sz_desc < 1) {
   1071         vars->sz_desc = 16;
   1072         vars->num_desc = 0;
   1073         vars->desc = _XkbTypedCalloc(vars->sz_desc, XkbRF_VarDescRec);
   1074     }
   1075     else if (vars->num_desc >= vars->sz_desc) {
   1076         vars->sz_desc *= 2;
   1077         vars->desc =
   1078             _XkbTypedReallocF(vars->desc, vars->sz_desc, XkbRF_VarDescRec);
   1079     }
   1080     if (!vars->desc) {
   1081         vars->sz_desc = vars->num_desc = 0;
   1082         PR_DEBUG("Allocation failure in XkbRF_AddVarDesc\n");
   1083         return NULL;
   1084     }
   1085     vars->desc[vars->num_desc].name = NULL;
   1086     vars->desc[vars->num_desc].desc = NULL;
   1087     return &vars->desc[vars->num_desc++];
   1088 }
   1089 
   1090 XkbRF_VarDescPtr
   1091 XkbRF_AddVarDescCopy(XkbRF_DescribeVarsPtr vars, XkbRF_VarDescPtr from)
   1092 {
   1093     XkbRF_VarDescPtr nd;
   1094 
   1095     if ((nd = XkbRF_AddVarDesc(vars)) != NULL) {
   1096         nd->name = _XkbDupString(from->name);
   1097         nd->desc = _XkbDupString(from->desc);
   1098     }
   1099     return nd;
   1100 }
   1101 
   1102 XkbRF_DescribeVarsPtr
   1103 XkbRF_AddVarToDescribe(XkbRF_RulesPtr rules, char *name)
   1104 {
   1105     if (rules->sz_extra < 1) {
   1106         rules->num_extra = 0;
   1107         rules->sz_extra = 1;
   1108         rules->extra_names = _XkbTypedCalloc(rules->sz_extra, char *);
   1109 
   1110         rules->extra = _XkbTypedCalloc(rules->sz_extra, XkbRF_DescribeVarsRec);
   1111     }
   1112     else if (rules->num_extra >= rules->sz_extra) {
   1113         rules->sz_extra *= 2;
   1114         rules->extra_names =
   1115             _XkbTypedReallocF(rules->extra_names, rules->sz_extra, char *);
   1116         rules->extra =
   1117             _XkbTypedReallocF(rules->extra, rules->sz_extra,
   1118                               XkbRF_DescribeVarsRec);
   1119     }
   1120     if ((!rules->extra_names) || (!rules->extra)) {
   1121         PR_DEBUG("allocation error in extra parts\n");
   1122         rules->sz_extra = rules->num_extra = 0;
   1123         free(rules->extra_names);
   1124         rules->extra_names = NULL;
   1125         free(rules->extra);
   1126         rules->extra = NULL;
   1127         return NULL;
   1128     }
   1129     rules->extra_names[rules->num_extra] = _XkbDupString(name);
   1130     memset(&rules->extra[rules->num_extra], 0, sizeof(XkbRF_DescribeVarsRec));
   1131     return &rules->extra[rules->num_extra++];
   1132 }
   1133 
   1134 Bool
   1135 XkbRF_LoadDescriptions(FILE *file, XkbRF_RulesPtr rules)
   1136 {
   1137     InputLine line;
   1138     XkbRF_VarDescRec tmp;
   1139     char *tok;
   1140     int len, headingtype, extra_ndx = 0;
   1141 
   1142     memset(&tmp, 0, sizeof(XkbRF_VarDescRec));
   1143     headingtype = HEAD_NONE;
   1144     InitInputLine(&line);
   1145     for (; GetInputLine(file, &line, False); line.num_line = 0) {
   1146         if (line.line[0] == '!') {
   1147             tok = strtok(&(line.line[1]), " \t");
   1148             if (strcmp(tok, "model") == 0)
   1149                 headingtype = HEAD_MODEL;
   1150             else if (_XkbStrCaseCmp(tok, "layout") == 0)
   1151                 headingtype = HEAD_LAYOUT;
   1152             else if (_XkbStrCaseCmp(tok, "variant") == 0)
   1153                 headingtype = HEAD_VARIANT;
   1154             else if (_XkbStrCaseCmp(tok, "option") == 0)
   1155                 headingtype = HEAD_OPTION;
   1156             else {
   1157                 int i;
   1158 
   1159                 headingtype = HEAD_EXTRA;
   1160                 extra_ndx = -1;
   1161                 for (i = 0; (i < rules->num_extra) && (extra_ndx < 0); i++) {
   1162                     if (_XkbStrCaseCmp(tok, rules->extra_names[i]))
   1163                         extra_ndx = i;
   1164                 }
   1165                 if (extra_ndx < 0) {
   1166                     XkbRF_DescribeVarsPtr var;
   1167 
   1168                     PR_DEBUG1("Extra heading \"%s\" encountered\n", tok);
   1169                     var = XkbRF_AddVarToDescribe(rules, tok);
   1170                     if (var)
   1171                         extra_ndx = var - rules->extra;
   1172                     else
   1173                         headingtype = HEAD_NONE;
   1174                 }
   1175             }
   1176             continue;
   1177         }
   1178 
   1179         if (headingtype == HEAD_NONE) {
   1180             PR_DEBUG("Must have a heading before first line of data\n");
   1181             PR_DEBUG("Illegal line of data ignored\n");
   1182             continue;
   1183         }
   1184 
   1185         len = strlen(line.line);
   1186         if ((tmp.name = strtok(line.line, " \t")) == NULL) {
   1187             PR_DEBUG("Huh? No token on line\n");
   1188             PR_DEBUG("Illegal line of data ignored\n");
   1189             continue;
   1190         }
   1191         if (strlen(tmp.name) == len) {
   1192             PR_DEBUG("No description found\n");
   1193             PR_DEBUG("Illegal line of data ignored\n");
   1194             continue;
   1195         }
   1196 
   1197         tok = line.line + strlen(tmp.name) + 1;
   1198         while ((*tok != '\n') && isspace(*tok))
   1199             tok++;
   1200         if (*tok == '\0') {
   1201             PR_DEBUG("No description found\n");
   1202             PR_DEBUG("Illegal line of data ignored\n");
   1203             continue;
   1204         }
   1205         tmp.desc = tok;
   1206         switch (headingtype) {
   1207         case HEAD_MODEL:
   1208             XkbRF_AddVarDescCopy(&rules->models, &tmp);
   1209             break;
   1210         case HEAD_LAYOUT:
   1211             XkbRF_AddVarDescCopy(&rules->layouts, &tmp);
   1212             break;
   1213         case HEAD_VARIANT:
   1214             XkbRF_AddVarDescCopy(&rules->variants, &tmp);
   1215             break;
   1216         case HEAD_OPTION:
   1217             XkbRF_AddVarDescCopy(&rules->options, &tmp);
   1218             break;
   1219         case HEAD_EXTRA:
   1220             XkbRF_AddVarDescCopy(&rules->extra[extra_ndx], &tmp);
   1221             break;
   1222         }
   1223     }
   1224     FreeInputLine(&line);
   1225     if ((rules->models.num_desc == 0) && (rules->layouts.num_desc == 0) &&
   1226         (rules->variants.num_desc == 0) && (rules->options.num_desc == 0) &&
   1227         (rules->num_extra == 0)) {
   1228         return False;
   1229     }
   1230     return True;
   1231 }
   1232 
   1233 Bool
   1234 XkbRF_LoadDescriptionsByName(char *base, char *locale, XkbRF_RulesPtr rules)
   1235 {
   1236     FILE *file;
   1237     char buf[PATH_MAX];
   1238     Bool ok;
   1239 
   1240     if ((!base) || (!rules))
   1241         return False;
   1242     if (locale) {
   1243         if (snprintf(buf, sizeof(buf), "%s-%s.lst", base, locale) >= (int) sizeof(buf))
   1244             return False;
   1245     }
   1246     else {
   1247         if (snprintf(buf, sizeof(buf), "%s.lst", base) >= (int) sizeof(buf))
   1248             return False;
   1249     }
   1250 
   1251     file = fopen(buf, "r" FOPEN_CLOEXEC);
   1252     if ((!file) && (locale)) {  /* fallback if locale was specified */
   1253         if (snprintf(buf, sizeof(buf), "%s.lst", base) >= (int) sizeof(buf))
   1254             return False;
   1255 
   1256         file = fopen(buf, "r" FOPEN_CLOEXEC);
   1257     }
   1258     if (!file)
   1259         return False;
   1260     ok = XkbRF_LoadDescriptions(file, rules);
   1261     fclose(file);
   1262     return ok;
   1263 }
   1264 
   1265 /***====================================================================***/
   1266 
   1267 XkbRF_RulesPtr
   1268 XkbRF_Load(char *base, char *locale, Bool wantDesc, Bool wantRules)
   1269 {
   1270     XkbRF_RulesPtr rules;
   1271 
   1272     if ((!base) || ((!wantDesc) && (!wantRules)))
   1273         return NULL;
   1274     if ((rules = _XkbTypedCalloc(1, XkbRF_RulesRec)) == NULL)
   1275         return NULL;
   1276     if (wantDesc && (!XkbRF_LoadDescriptionsByName(base, locale, rules))) {
   1277         XkbRF_Free(rules, True);
   1278         return NULL;
   1279     }
   1280     if (wantRules && (!XkbRF_LoadRulesByName(base, locale, rules))) {
   1281         XkbRF_Free(rules, True);
   1282         return NULL;
   1283     }
   1284     return rules;
   1285 }
   1286 
   1287 XkbRF_RulesPtr
   1288 XkbRF_Create(int szRules, int szExtra)
   1289 {
   1290     XkbRF_RulesPtr rules;
   1291 
   1292     if ((rules = _XkbTypedCalloc(1, XkbRF_RulesRec)) == NULL)
   1293         return NULL;
   1294     if (szRules > 0) {
   1295         rules->sz_rules = szRules;
   1296         rules->rules = _XkbTypedCalloc(rules->sz_rules, XkbRF_RuleRec);
   1297         if (!rules->rules) {
   1298             _XkbFree(rules);
   1299             return NULL;
   1300         }
   1301     }
   1302     if (szExtra > 0) {
   1303         rules->sz_extra = szExtra;
   1304         rules->extra = _XkbTypedCalloc(rules->sz_extra, XkbRF_DescribeVarsRec);
   1305         if (!rules->extra) {
   1306             if (rules->rules)
   1307                 _XkbFree(rules->rules);
   1308             _XkbFree(rules);
   1309             return NULL;
   1310         }
   1311     }
   1312     return rules;
   1313 }
   1314 
   1315 /***====================================================================***/
   1316 
   1317 static void
   1318 XkbRF_ClearVarDescriptions(XkbRF_DescribeVarsPtr var)
   1319 {
   1320     register int i;
   1321 
   1322     for (i = 0; i < var->num_desc; i++) {
   1323         if (var->desc[i].name)
   1324             _XkbFree(var->desc[i].name);
   1325         if (var->desc[i].desc)
   1326             _XkbFree(var->desc[i].desc);
   1327         var->desc[i].name = var->desc[i].desc = NULL;
   1328     }
   1329     if (var->desc)
   1330         _XkbFree(var->desc);
   1331     var->desc = NULL;
   1332     return;
   1333 }
   1334 
   1335 void
   1336 XkbRF_Free(XkbRF_RulesPtr rules, Bool freeRules)
   1337 {
   1338     int i;
   1339     XkbRF_RulePtr rule;
   1340     XkbRF_GroupPtr group;
   1341 
   1342     if (!rules)
   1343         return;
   1344     XkbRF_ClearVarDescriptions(&rules->models);
   1345     XkbRF_ClearVarDescriptions(&rules->layouts);
   1346     XkbRF_ClearVarDescriptions(&rules->variants);
   1347     XkbRF_ClearVarDescriptions(&rules->options);
   1348     if (rules->extra) {
   1349         for (i = 0; i < rules->num_extra; i++) {
   1350             XkbRF_ClearVarDescriptions(&rules->extra[i]);
   1351         }
   1352         _XkbFree(rules->extra);
   1353         rules->num_extra = rules->sz_extra = 0;
   1354         rules->extra = NULL;
   1355     }
   1356     if (rules->rules) {
   1357         for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) {
   1358             if (rule->model)
   1359                 _XkbFree(rule->model);
   1360             if (rule->layout)
   1361                 _XkbFree(rule->layout);
   1362             if (rule->variant)
   1363                 _XkbFree(rule->variant);
   1364             if (rule->option)
   1365                 _XkbFree(rule->option);
   1366             if (rule->keycodes)
   1367                 _XkbFree(rule->keycodes);
   1368             if (rule->symbols)
   1369                 _XkbFree(rule->symbols);
   1370             if (rule->types)
   1371                 _XkbFree(rule->types);
   1372             if (rule->compat)
   1373                 _XkbFree(rule->compat);
   1374             if (rule->geometry)
   1375                 _XkbFree(rule->geometry);
   1376             if (rule->keymap)
   1377                 _XkbFree(rule->keymap);
   1378             memset(rule, 0, sizeof(XkbRF_RuleRec));
   1379         }
   1380         _XkbFree(rules->rules);
   1381         rules->num_rules = rules->sz_rules = 0;
   1382         rules->rules = NULL;
   1383     }
   1384 
   1385     if (rules->groups) {
   1386         for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
   1387             if (group->name)
   1388                 _XkbFree(group->name);
   1389             if (group->words)
   1390                 _XkbFree(group->words);
   1391         }
   1392         _XkbFree(rules->groups);
   1393         rules->num_groups = 0;
   1394         rules->groups = NULL;
   1395     }
   1396     if (freeRules)
   1397         _XkbFree(rules);
   1398     return;
   1399 }
   1400 
   1401 
   1402 Bool
   1403 XkbRF_GetNamesProp(Display * dpy, char **rf_rtrn, XkbRF_VarDefsPtr vd_rtrn)
   1404 {
   1405     Atom rules_atom, actual_type;
   1406     int fmt;
   1407     unsigned long nitems, bytes_after;
   1408     unsigned char *data;
   1409     char *out, *end;
   1410     Status rtrn;
   1411 
   1412     rules_atom = XInternAtom(dpy, _XKB_RF_NAMES_PROP_ATOM, True);
   1413     if (rules_atom == None)     /* property cannot exist */
   1414         return False;
   1415     rtrn = XGetWindowProperty(dpy, DefaultRootWindow(dpy), rules_atom,
   1416                               0L, _XKB_RF_NAMES_PROP_MAXLEN, False,
   1417                               XA_STRING, &actual_type,
   1418                               &fmt, &nitems, &bytes_after,
   1419                               &data);
   1420     if (rtrn != Success)
   1421         return False;
   1422     if (rf_rtrn)
   1423         *rf_rtrn = NULL;
   1424     (void) memset(vd_rtrn, 0, sizeof(XkbRF_VarDefsRec));
   1425     if ((bytes_after > 0) || (actual_type != XA_STRING) || (fmt != 8)) {
   1426         if (data)
   1427             XFree(data);
   1428         return (fmt == 0 ? True : False);
   1429     }
   1430 
   1431     out = (char *) data;
   1432     end = out + nitems;
   1433     if (out && (*out) && rf_rtrn)
   1434         *rf_rtrn = _XkbDupString(out);
   1435     out += strlen(out) + 1;
   1436 
   1437     if (out < end) {
   1438         if (*out)
   1439             vd_rtrn->model = _XkbDupString(out);
   1440         out += strlen(out) + 1;
   1441     }
   1442 
   1443     if (out < end) {
   1444         if (*out)
   1445             vd_rtrn->layout = _XkbDupString(out);
   1446         out += strlen(out) + 1;
   1447     }
   1448 
   1449     if (out < end) {
   1450         if (*out)
   1451             vd_rtrn->variant = _XkbDupString(out);
   1452         out += strlen(out) + 1;
   1453     }
   1454 
   1455     if (out < end) {
   1456         if (*out)
   1457             vd_rtrn->options = _XkbDupString(out);
   1458         out += strlen(out) + 1;
   1459     }
   1460     /*
   1461      * For now extra_options and extra_rules are not used
   1462      * update XkbRF_FreeVarDefs if that changes
   1463      */
   1464     XFree(data);
   1465     return True;
   1466 }
   1467 
   1468 Bool
   1469 XkbRF_SetNamesProp(Display *dpy, char *rules_file, XkbRF_VarDefsPtr var_defs)
   1470 {
   1471     int len, out;
   1472     Atom name;
   1473     char *pval;
   1474 
   1475     len = (rules_file ? strlen(rules_file) : 0);
   1476     len += (var_defs->model ? strlen(var_defs->model) : 0);
   1477     len += (var_defs->layout ? strlen(var_defs->layout) : 0);
   1478     len += (var_defs->variant ? strlen(var_defs->variant) : 0);
   1479     len += (var_defs->options ? strlen(var_defs->options) : 0);
   1480     if (len < 1)
   1481         return True;
   1482 
   1483     len += 5;                   /* trailing NULs */
   1484 
   1485     name = XInternAtom(dpy, _XKB_RF_NAMES_PROP_ATOM, False);
   1486     if (name == None) {         /* should never happen */
   1487         _XkbLibError(_XkbErrXReqFailure, "XkbRF_SetNamesProp", X_InternAtom);
   1488         return False;
   1489     }
   1490     pval = (char *) _XkbAlloc(len);
   1491     if (!pval) {
   1492         _XkbLibError(_XkbErrBadAlloc, "XkbRF_SetNamesProp", len);
   1493         return False;
   1494     }
   1495     out = 0;
   1496     if (rules_file) {
   1497         strcpy(&pval[out], rules_file);
   1498         out += strlen(rules_file);
   1499     }
   1500     pval[out++] = '\0';
   1501     if (var_defs->model) {
   1502         strcpy(&pval[out], var_defs->model);
   1503         out += strlen(var_defs->model);
   1504     }
   1505     pval[out++] = '\0';
   1506     if (var_defs->layout) {
   1507         strcpy(&pval[out], var_defs->layout);
   1508         out += strlen(var_defs->layout);
   1509     }
   1510     pval[out++] = '\0';
   1511     if (var_defs->variant) {
   1512         strcpy(&pval[out], var_defs->variant);
   1513         out += strlen(var_defs->variant);
   1514     }
   1515     pval[out++] = '\0';
   1516     if (var_defs->options) {
   1517         strcpy(&pval[out], var_defs->options);
   1518         out += strlen(var_defs->options);
   1519     }
   1520     pval[out++] = '\0';
   1521     if (out != len) {
   1522         _XkbLibError(_XkbErrBadLength, "XkbRF_SetNamesProp", out);
   1523         _XkbFree(pval);
   1524         return False;
   1525     }
   1526 
   1527     XChangeProperty(dpy, DefaultRootWindow(dpy), name, XA_STRING, 8,
   1528                     PropModeReplace, (unsigned char *) pval, len);
   1529     _XkbFree(pval);
   1530     return True;
   1531 }
   1532 
   1533 void
   1534 XkbRF_FreeVarDefs(XkbRF_VarDefsPtr var_defs, Bool freeVarDefs)
   1535 {
   1536     if (!var_defs)
   1537         return;
   1538 
   1539     _XkbFree(var_defs->model);
   1540     var_defs->model = NULL;
   1541     _XkbFree(var_defs->layout);
   1542     var_defs->layout = NULL;
   1543     _XkbFree(var_defs->variant);
   1544     var_defs->variant = NULL;
   1545     _XkbFree(var_defs->options);
   1546     var_defs->options = NULL;
   1547 
   1548     _XkbFree(var_defs->extra_names);
   1549     var_defs->extra_names = NULL;
   1550     _XkbFree(var_defs->extra_values);
   1551     var_defs->extra_values = NULL;
   1552 
   1553     if(freeVarDefs)
   1554         _XkbFree(var_defs);
   1555 }
   1556