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