Add rules file parsing from xkbfile
authorDan Nicholson <dbn.lists@gmail.com>
Sun, 29 Mar 2009 18:25:44 +0000 (11:25 -0700)
committerDan Nicholson <dbn.lists@gmail.com>
Mon, 30 Mar 2009 13:31:26 +0000 (06:31 -0700)
Copy over the Xkb_RF* rules parsing functions from xkbfile's maprules.c.
Eventually, this will be tied into xkbcomp's path searching utilities so
you don't need to supply a full path to the rules file. Also, it this
should eventually incorporate the server's RMLVOSet.

include/X11/extensions/XKBcommon.h
src/Makefile.am
src/XKBcommonint.h
src/maprules.c [new file with mode: 0644]

index f751be6..80a5e46 100644 (file)
@@ -28,12 +28,14 @@ authorization from the authors.
 #ifndef _XKBCOMMON_H_
 #define _XKBCOMMON_H_
 
+#include <stdio.h>
 #include <X11/X.h>
 #include <X11/Xdefs.h>
 #include <X11/Xfuncproto.h>
 #include <X11/keysym.h>
 #include <X11/extensions/XKBstrcommon.h>
 #include <X11/extensions/XKBgeomcommon.h>
+#include <X11/extensions/XKBrulescommon.h>
 
 /* Common keyboard description structure */
 typedef struct _XkbcDesc {
@@ -316,6 +318,46 @@ XkbcComputeRowBounds(XkbGeometryPtr geom, XkbSectionPtr section, XkbRowPtr row);
 extern Bool
 XkbcComputeSectionBounds(XkbGeometryPtr geom, XkbSectionPtr section);
 
+extern Bool
+XkbcRF_GetComponents(XkbRF_RulesPtr rules, XkbRF_VarDefsPtr defs,
+                     XkbComponentNamesPtr names);
+
+extern XkbRF_RulePtr
+XkbcRF_AddRule(XkbRF_RulesPtr rules);
+
+extern XkbRF_GroupPtr
+XkbcRF_AddGroup(XkbRF_RulesPtr rules);
+
+extern Bool
+XkbcRF_LoadRules(FILE *file, XkbRF_RulesPtr rules);
+
+extern Bool
+XkbcRF_LoadRulesByName(char *base, char *locale, XkbRF_RulesPtr rules);
+
+extern XkbRF_VarDescPtr
+XkbcRF_AddVarDesc(XkbRF_DescribeVarsPtr vars);
+
+extern XkbRF_VarDescPtr
+XkbcRF_AddVarDescCopy(XkbRF_DescribeVarsPtr vars, XkbRF_VarDescPtr from);
+
+extern XkbRF_DescribeVarsPtr
+XkbcRF_AddVarToDescribe(XkbRF_RulesPtr rules, char *name);
+
+extern Bool
+XkbcRF_LoadDescriptions(FILE *file, XkbRF_RulesPtr rules);
+
+extern Bool
+XkbcRF_LoadDescriptionsByName(char *base, char *locale, XkbRF_RulesPtr rules);
+
+extern XkbRF_RulesPtr
+XkbcRF_Load(char *base, char *locale, Bool wantDesc, Bool wantRules);
+
+extern XkbRF_RulesPtr
+XkbcRF_Create(int szRules, int szExtra);
+
+extern void
+XkbcRF_Free(XkbRF_RulesPtr rules, Bool freeRules);
+
 extern int
 XkbcInitCanonicalKeyTypes(XkbcDescPtr xkb, unsigned which, int keypadVMod);
 
index c265fb3..f165e91 100644 (file)
@@ -13,6 +13,7 @@ libxkbcommon_la_SOURCES = \
        geom.c \
        keysym.c \
        malloc.c \
+       maprules.c \
        misc.c \
        text.c \
        xkb.c
index 1007ed7..0c026bb 100644 (file)
@@ -55,4 +55,7 @@ authorization from the authors.
 #define _XkbClearElems(a,f,l,t) bzero(&(a)[f],((l)-(f)+1)*sizeof(t))
 #define _XkbFree(p)             free((p))
 
+#define _XkbDupString(s)        ((s) ? strdup(s) : NULL)
+#define _XkbStrCaseCmp          strcasecmp
+
 #endif /* _XKBCOMMONINT_H_ */
diff --git a/src/maprules.c b/src/maprules.c
new file mode 100644 (file)
index 0000000..c0c17c0
--- /dev/null
@@ -0,0 +1,1324 @@
+/************************************************************
+ Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "X11/extensions/XKBcommon.h"
+#include "XKBcommonint.h"
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#elif defined(HAVE_CONFIG_H)
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#define X_INCLUDE_STRING_H
+#define XOS_USE_NO_LOCKING
+#include <X11/Xos_r.h>
+
+#include <X11/Xproto.h>
+#include <X11/X.h>
+#include <X11/Xos.h>
+#include <X11/Xfuncs.h>
+
+#ifdef DEBUG
+#define PR_DEBUG(s)            fprintf(stderr,s)
+#define PR_DEBUG1(s,a)         fprintf(stderr,s,a)
+#define PR_DEBUG2(s,a,b)       fprintf(stderr,s,a,b)
+#else
+#define PR_DEBUG(s)
+#define PR_DEBUG1(s,a)
+#define PR_DEBUG2(s,a,b)
+#endif
+
+/***====================================================================***/
+
+#define DFLT_LINE_SIZE 128
+
+typedef struct {
+       int     line_num;
+       int     sz_line;
+       int     num_line;
+       char    buf[DFLT_LINE_SIZE];
+       char *  line;
+} InputLine;
+
+static void
+InitInputLine(InputLine *line)
+{
+    line->line_num= 1;
+    line->num_line= 0;
+    line->sz_line= DFLT_LINE_SIZE;
+    line->line=        line->buf;
+    return;
+}
+
+static void
+FreeInputLine(InputLine *line)
+{
+    if (line->line!=line->buf)
+       _XkbFree(line->line);
+    line->line_num= 1;
+    line->num_line= 0;
+    line->sz_line= DFLT_LINE_SIZE;
+    line->line= line->buf;
+    return;
+}
+
+static int
+InputLineAddChar(InputLine *line,int ch)
+{
+    if (line->num_line>=line->sz_line) {
+       if (line->line==line->buf) {
+           line->line= (char *)_XkbAlloc(line->sz_line*2);
+           memcpy(line->line,line->buf,line->sz_line);
+       }
+       else {
+           line->line=(char *)_XkbRealloc((char *)line->line,line->sz_line*2);
+       }
+       line->sz_line*= 2;
+    }
+    line->line[line->num_line++]= ch;
+    return ch;
+}
+
+#define        ADD_CHAR(l,c)   ((l)->num_line<(l)->sz_line?\
+                               (int)((l)->line[(l)->num_line++]= (c)):\
+                               InputLineAddChar(l,c))
+
+static Bool
+GetInputLine(FILE *file,InputLine *line,Bool checkbang)
+{
+int    ch;
+Bool   endOfFile,spacePending,slashPending,inComment;
+
+     endOfFile= False;
+     while ((!endOfFile)&&(line->num_line==0)) {
+       spacePending= slashPending= inComment= False;
+       while (((ch=getc(file))!='\n')&&(ch!=EOF)) {
+           if (ch=='\\') {
+               if ((ch=getc(file))==EOF)
+                   break;
+               if (ch=='\n') {
+                   inComment= False;
+                   ch= ' ';
+                   line->line_num++;
+               }
+           }
+           if (inComment)
+               continue;
+           if (ch=='/') {
+               if (slashPending) {
+                   inComment= True;
+                   slashPending= False;
+               }
+               else {
+                   slashPending= True;
+               }
+               continue;
+           }
+           else if (slashPending) {
+               if (spacePending) {
+                   ADD_CHAR(line,' ');
+                   spacePending= False;
+               }
+               ADD_CHAR(line,'/');
+               slashPending= False;
+           }
+           if (isspace(ch)) {
+               while (isspace(ch)&&(ch!='\n')&&(ch!=EOF)) {
+                   ch= getc(file);
+               }
+               if (ch==EOF)
+                   break;
+               if ((ch!='\n')&&(line->num_line>0))
+                   spacePending= True;
+               ungetc(ch,file);
+           }
+           else {
+               if (spacePending) {
+                   ADD_CHAR(line,' ');
+                   spacePending= False;
+               }
+               if (checkbang && ch=='!') {
+                   if (line->num_line!=0) {
+                       PR_DEBUG("The '!' legal only at start of line\n");
+                       PR_DEBUG("Line containing '!' ignored\n");
+                       line->num_line= 0;
+                       inComment= 0;
+                       break;
+                   }
+
+               }
+               ADD_CHAR(line,ch);
+           }
+       }
+       if (ch==EOF)
+            endOfFile= True;
+/*     else line->num_line++;*/
+     }
+     if ((line->num_line==0)&&(endOfFile))
+       return False;
+      ADD_CHAR(line,'\0');
+      return True;
+}
+
+/***====================================================================***/
+
+#define        MODEL           0
+#define        LAYOUT          1
+#define        VARIANT         2
+#define        OPTION          3
+#define        KEYCODES        4
+#define SYMBOLS                5
+#define        TYPES           6
+#define        COMPAT          7
+#define        GEOMETRY        8
+#define        KEYMAP          9
+#define        MAX_WORDS       10
+
+#define        PART_MASK       0x000F
+#define        COMPONENT_MASK  0x03F0
+
+static char *  cname[MAX_WORDS] = {
+       "model", "layout", "variant", "option", 
+       "keycodes", "symbols", "types", "compat", "geometry", "keymap"
+};
+
+typedef        struct _RemapSpec {
+       int                     number;
+       int                     num_remap;
+       struct  {
+               int     word;
+               int     index;
+                }              remap[MAX_WORDS];
+} RemapSpec;
+
+typedef struct _FileSpec {
+       char *                  name[MAX_WORDS];
+       struct _FileSpec *      pending;
+} FileSpec;
+
+typedef struct {
+       char *                  model;
+       char *                  layout[XkbNumKbdGroups+1];
+       char *                  variant[XkbNumKbdGroups+1];
+       char *                  options;
+} XkbRF_MultiDefsRec, *XkbRF_MultiDefsPtr;
+
+#define NDX_BUFF_SIZE  4
+
+/***====================================================================***/
+
+static char*
+get_index(char *str, int *ndx)
+{
+   char ndx_buf[NDX_BUFF_SIZE];
+   char *end;
+
+   if (*str != '[') {
+       *ndx = 0;
+       return str;
+   }
+   str++;
+   end = strchr(str, ']');
+   if (end == NULL) {
+       *ndx = -1;
+       return str - 1;
+   }
+   if ( (end - str) >= NDX_BUFF_SIZE) {
+       *ndx = -1;
+       return end + 1;
+   }
+   strncpy(ndx_buf, str, end - str);
+   ndx_buf[end - str] = '\0';
+   *ndx = atoi(ndx_buf);
+   return end + 1;
+}
+
+static void
+SetUpRemap(InputLine *line,RemapSpec *remap)
+{
+char *         tok,*str;
+unsigned       present, l_ndx_present, v_ndx_present;
+register int   i;
+int            len, ndx;
+_Xstrtokparams strtok_buf;
+#ifdef DEBUG
+Bool           found;
+#endif
+
+
+   l_ndx_present = v_ndx_present = present= 0;
+   str= &line->line[1];
+   len = remap->number;
+   bzero((char *)remap,sizeof(RemapSpec));
+   remap->number = len;
+   while ((tok=_XStrtok(str," ",strtok_buf))!=NULL) {
+#ifdef DEBUG
+       found= False;
+#endif
+       str= NULL;
+       if (strcmp(tok,"=")==0)
+           continue;
+       for (i=0;i<MAX_WORDS;i++) {
+            len = strlen(cname[i]);
+           if (strncmp(cname[i],tok,len)==0) {
+               if(strlen(tok) > len) {
+                   char *end = get_index(tok+len, &ndx);
+                   if ((i != LAYOUT && i != VARIANT) ||
+                       *end != '\0' || ndx == -1)
+                       break;
+                    if (ndx < 1 || ndx > XkbNumKbdGroups) {
+                       PR_DEBUG2("Illegal %s index: %d\n", cname[i], ndx);
+                       PR_DEBUG1("Index must be in range 1..%d\n",
+                                  XkbNumKbdGroups);
+                       break;
+                    }
+                } else {
+                   ndx = 0;
+                }
+#ifdef DEBUG
+               found= True;
+#endif
+               if (present&(1<<i)) {
+                   if ((i == LAYOUT && l_ndx_present&(1<<ndx)) ||
+                       (i == VARIANT && v_ndx_present&(1<<ndx)) ) {
+                       PR_DEBUG1("Component \"%s\" listed twice\n",tok);
+                       PR_DEBUG("Second definition ignored\n");
+                       break;
+                   }
+               }
+               present |= (1<<i);
+                if (i == LAYOUT)
+                    l_ndx_present |= 1 << ndx;
+                if (i == VARIANT)
+                    v_ndx_present |= 1 << ndx;
+               remap->remap[remap->num_remap].word= i;
+               remap->remap[remap->num_remap++].index= ndx;
+               break;
+           }
+       }
+#ifdef DEBUG
+       if (!found) {
+           fprintf(stderr,"Unknown component \"%s\" ignored\n",tok);
+       }
+#endif
+   }
+   if ((present&PART_MASK)==0) {
+#ifdef DEBUG
+       unsigned mask= PART_MASK;
+       fprintf(stderr,"Mapping needs at least one of ");
+       for (i=0; (i<MAX_WORDS); i++) {
+           if ((1L<<i)&mask) {
+               mask&= ~(1L<<i);
+               if (mask)       fprintf(stderr,"\"%s,\" ",cname[i]);
+               else            fprintf(stderr,"or \"%s\"\n",cname[i]);
+           }
+       }
+       fprintf(stderr,"Illegal mapping ignored\n");
+#endif
+       remap->num_remap= 0;
+       return;
+   }
+   if ((present&COMPONENT_MASK)==0) {
+       PR_DEBUG("Mapping needs at least one component\n");
+       PR_DEBUG("Illegal mapping ignored\n");
+       remap->num_remap= 0;
+       return;
+   }
+   if (((present&COMPONENT_MASK)&(1<<KEYMAP))&&
+                               ((present&COMPONENT_MASK)!=(1<<KEYMAP))) {
+       PR_DEBUG("Keymap cannot appear with other components\n");
+       PR_DEBUG("Illegal mapping ignored\n");
+       remap->num_remap= 0;
+       return;
+   }
+   remap->number++;
+   return;
+}
+
+static Bool
+MatchOneOf(char *wanted,char *vals_defined)
+{
+char   *str,*next;
+int    want_len= strlen(wanted);
+
+    for (str=vals_defined,next=NULL;str!=NULL;str=next) {
+       int len;
+       next= strchr(str,',');
+       if (next) {
+           len= next-str;
+           next++;
+       }
+       else {
+           len= strlen(str);
+       }
+       if ((len==want_len)&&(strncmp(wanted,str,len)==0))
+           return True;
+    }
+    return False;
+}
+
+/***====================================================================***/
+
+static Bool
+CheckLine(     InputLine *             line,
+               RemapSpec *             remap,
+               XkbRF_RulePtr           rule,
+               XkbRF_GroupPtr          group)
+{
+char *         str,*tok;
+register int   nread, i;
+FileSpec       tmp;
+_Xstrtokparams strtok_buf;
+Bool           append = False;
+
+    if (line->line[0]=='!') {
+        if (line->line[1] == '$' ||
+            (line->line[1] == ' ' && line->line[2] == '$')) {
+            char *gname = strchr(line->line, '$');
+            char *words = strchr(gname, ' ');
+            if(!words)
+                return False;
+            *words++ = '\0';
+            for (; *words; words++) {
+                if (*words != '=' && *words != ' ')
+                    break;
+            }
+            if (*words == '\0')
+                return False;
+            group->name = _XkbDupString(gname);
+            group->words = _XkbDupString(words);
+            for (i = 1, words = group->words; *words; words++) {
+                 if ( *words == ' ') {
+                     *words++ = '\0';
+                     i++;
+                 }
+            }
+            group->number = i;
+            return True;
+        } else {
+           SetUpRemap(line,remap);
+           return False;
+        }
+    }
+
+    if (remap->num_remap==0) {
+       PR_DEBUG("Must have a mapping before first line of data\n");
+       PR_DEBUG("Illegal line of data ignored\n");
+       return False;
+    }
+    bzero((char *)&tmp,sizeof(FileSpec));
+    str= line->line;
+    for (nread= 0;(tok=_XStrtok(str," ",strtok_buf))!=NULL;nread++) {
+       str= NULL;
+       if (strcmp(tok,"=")==0) {
+           nread--;
+           continue;
+       }
+       if (nread>remap->num_remap) {
+           PR_DEBUG("Too many words on a line\n");
+           PR_DEBUG1("Extra word \"%s\" ignored\n",tok);
+           continue;
+       }
+       tmp.name[remap->remap[nread].word]= tok;
+       if (*tok == '+' || *tok == '|')
+           append = True;
+    }
+    if (nread<remap->num_remap) {
+       PR_DEBUG1("Too few words on a line: %s\n", line->line);
+       PR_DEBUG("line ignored\n");
+       return False;
+    }
+
+    rule->flags= 0;
+    rule->number = remap->number;
+    if (tmp.name[OPTION])
+        rule->flags|= XkbRF_Option;
+    else if (append)
+        rule->flags|= XkbRF_Append;
+    else
+        rule->flags|= XkbRF_Normal;
+    rule->model= _XkbDupString(tmp.name[MODEL]);
+    rule->layout= _XkbDupString(tmp.name[LAYOUT]);
+    rule->variant= _XkbDupString(tmp.name[VARIANT]);
+    rule->option= _XkbDupString(tmp.name[OPTION]);
+
+    rule->keycodes= _XkbDupString(tmp.name[KEYCODES]);
+    rule->symbols= _XkbDupString(tmp.name[SYMBOLS]);
+    rule->types= _XkbDupString(tmp.name[TYPES]);
+    rule->compat= _XkbDupString(tmp.name[COMPAT]);
+    rule->geometry= _XkbDupString(tmp.name[GEOMETRY]);
+    rule->keymap= _XkbDupString(tmp.name[KEYMAP]);
+
+    rule->layout_num = rule->variant_num = 0;
+    for (i = 0; i < nread; i++) {
+        if (remap->remap[i].index) {
+           if (remap->remap[i].word == LAYOUT)
+               rule->layout_num = remap->remap[i].index;
+           if (remap->remap[i].word == VARIANT)
+               rule->variant_num = remap->remap[i].index;
+        }
+    }
+    return True;
+}
+
+static char *
+_Concat(char *str1,char *str2)
+{
+int len;
+
+    if ((!str1)||(!str2))
+       return str1;
+    len= strlen(str1)+strlen(str2)+1;
+    str1= _XkbTypedRealloc(str1,len,char);
+    if (str1)
+       strcat(str1,str2);
+    return str1;
+}
+
+static void
+squeeze_spaces(char *p1)
+{
+   char *p2;
+   for (p2 = p1; *p2; p2++) {
+       *p1 = *p2;
+       if (*p1 != ' ') p1++;
+   }
+   *p1 = '\0';
+}
+
+static Bool
+MakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs)
+{
+
+   bzero((char *)mdefs,sizeof(XkbRF_MultiDefsRec));
+   mdefs->model = defs->model;
+   mdefs->options = _XkbDupString(defs->options);
+   if (mdefs->options) squeeze_spaces(mdefs->options); 
+
+   if (defs->layout) {
+       if (!strchr(defs->layout, ',')) {
+           mdefs->layout[0] = defs->layout;
+       } else {
+           char *p;
+           int i;
+           mdefs->layout[1] = _XkbDupString(defs->layout);
+          if (mdefs->layout[1] == NULL)
+             return False;
+           squeeze_spaces(mdefs->layout[1]);
+           p = mdefs->layout[1];
+           for (i = 2; i <= XkbNumKbdGroups; i++) {
+              if ((p = strchr(p, ','))) {
+                 *p++ = '\0';
+                 mdefs->layout[i] = p;
+              } else {
+                 break;
+              }
+           }
+           if (p && (p = strchr(p, ',')))
+              *p = '\0';
+       }
+   }
+
+   if (defs->variant) {
+       if (!strchr(defs->variant, ',')) {
+           mdefs->variant[0] = defs->variant;
+       } else {
+           char *p;
+           int i;
+           mdefs->variant[1] = _XkbDupString(defs->variant);
+          if (mdefs->variant[1] == NULL)
+             return False;
+           squeeze_spaces(mdefs->variant[1]);
+           p = mdefs->variant[1];
+           for (i = 2; i <= XkbNumKbdGroups; i++) {
+              if ((p = strchr(p, ','))) {
+                 *p++ = '\0';
+                 mdefs->variant[i] = p;
+              } else {
+                 break;
+              }
+           }
+           if (p && (p = strchr(p, ',')))
+              *p = '\0';
+       }
+   }
+   return True;
+}
+
+static void
+FreeMultiDefs(XkbRF_MultiDefsPtr defs)
+{
+  if (defs->options) _XkbFree(defs->options);
+  if (defs->layout[1])  _XkbFree(defs->layout[1]);
+  if (defs->variant[1])  _XkbFree(defs->variant[1]);
+}
+
+static void
+Apply(char *src, char **dst)
+{
+    if (src) {
+        if (*src == '+' || *src == '!') {
+           *dst= _Concat(*dst, src);
+        } else {
+            if (*dst == NULL)
+               *dst= _XkbDupString(src);
+        }
+    }
+}
+
+static void
+XkbRF_ApplyRule(       XkbRF_RulePtr           rule,
+                       XkbComponentNamesPtr    names)
+{
+    rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
+
+    Apply(rule->keycodes, &names->keycodes);
+    Apply(rule->symbols,  &names->symbols);
+    Apply(rule->types,    &names->types);
+    Apply(rule->compat,   &names->compat);
+    Apply(rule->geometry, &names->geometry);
+    Apply(rule->keymap,   &names->keymap);
+}
+
+static Bool
+CheckGroup(    XkbRF_RulesPtr          rules,
+               char *                  group_name,
+               char *                  name)
+{
+   int i;
+   char *p;
+   XkbRF_GroupPtr group;
+
+   for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
+       if (! strcmp(group->name, group_name)) {
+           break;
+       }
+   }
+   if (i == rules->num_groups)
+       return False;
+   for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
+       if (! strcmp(p, name)) {
+           return True;
+       }
+   }
+   return False;
+}
+
+static int
+XkbRF_CheckApplyRule(  XkbRF_RulePtr           rule,
+                       XkbRF_MultiDefsPtr      mdefs,
+                       XkbComponentNamesPtr    names,
+                       XkbRF_RulesPtr          rules)
+{
+    Bool pending = False;
+
+    if (rule->model != NULL) {
+        if(mdefs->model == NULL)
+            return 0;
+        if (strcmp(rule->model, "*") == 0) {
+            pending = True;
+        } else {
+            if (rule->model[0] == '$') {
+               if (!CheckGroup(rules, rule->model, mdefs->model))
+                  return 0;
+            } else {
+              if (strcmp(rule->model, mdefs->model) != 0)
+                 return 0;
+           }
+       }
+    }
+    if (rule->option != NULL) {
+       if (mdefs->options == NULL)
+           return 0;
+       if ((!MatchOneOf(rule->option,mdefs->options)))
+           return 0;
+    }
+
+    if (rule->layout != NULL) {
+       if(mdefs->layout[rule->layout_num] == NULL ||
+          *mdefs->layout[rule->layout_num] == '\0')
+           return 0;
+        if (strcmp(rule->layout, "*") == 0) {
+            pending = True;
+        } else {
+            if (rule->layout[0] == '$') {
+               if (!CheckGroup(rules, rule->layout,
+                               mdefs->layout[rule->layout_num]))
+                  return 0;
+           } else {
+              if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
+                  return 0;
+           }
+       }
+    }
+    if (rule->variant != NULL) {
+       if (mdefs->variant[rule->variant_num] == NULL ||
+           *mdefs->variant[rule->variant_num] == '\0')
+           return 0;
+        if (strcmp(rule->variant, "*") == 0) {
+            pending = True;
+        } else {
+            if (rule->variant[0] == '$') {
+               if (!CheckGroup(rules, rule->variant,
+                               mdefs->variant[rule->variant_num]))
+                  return 0;
+            } else {
+              if (strcmp(rule->variant,
+                          mdefs->variant[rule->variant_num]) != 0)
+                  return 0;
+           }
+       }
+    }
+    if (pending) {
+        rule->flags|= XkbRF_PendingMatch;
+       return rule->number;
+    }
+    /* exact match, apply it now */
+    XkbRF_ApplyRule(rule,names);
+    return rule->number;
+}
+
+static void
+XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
+{
+register int   i;
+XkbRF_RulePtr  rule;
+
+    for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
+       rule->flags&= ~XkbRF_PendingMatch;
+    }
+}
+
+static void
+XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules,XkbComponentNamesPtr names)
+{
+int            i;
+XkbRF_RulePtr  rule;
+
+    for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
+       if ((rule->flags&XkbRF_PendingMatch)==0)
+           continue;
+       XkbRF_ApplyRule(rule,names);
+    }
+}
+
+static void
+XkbRF_CheckApplyRules( XkbRF_RulesPtr          rules,
+                       XkbRF_MultiDefsPtr      mdefs,
+                       XkbComponentNamesPtr    names,
+                       int                     flags)
+{
+int            i;
+XkbRF_RulePtr  rule;
+int            skip;
+
+    for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
+       if ((rule->flags & flags) != flags)
+           continue;
+       skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
+       if (skip && !(flags & XkbRF_Option)) {
+           for ( ;(i < rules->num_rules) && (rule->number == skip);
+                 rule++, i++);
+           rule--; i--;
+       }
+    }
+}
+
+/***====================================================================***/
+
+static char *
+XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
+{
+char   *str, *outstr, *orig, *var;
+int    len, ndx;
+
+    orig= name;
+    str= index(name,'%');
+    if (str==NULL)
+       return name;
+    len= strlen(name);
+    while (str!=NULL) {
+       char pfx= str[1];
+       int   extra_len= 0;
+       if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
+           extra_len= 1;
+           str++;
+       }
+       else if (pfx=='(') {
+           extra_len= 2;
+           str++;
+       }
+       var = str + 1;
+       str = get_index(var + 1, &ndx);
+       if (ndx == -1) {
+           str = index(str,'%');
+           continue;
+        }
+       if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
+           len+= strlen(mdefs->layout[ndx])+extra_len;
+       else if ((*var=='m')&&mdefs->model)
+           len+= strlen(mdefs->model)+extra_len;
+       else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
+           len+= strlen(mdefs->variant[ndx])+extra_len;
+       if ((pfx=='(')&&(*str==')')) {
+           str++;
+       }
+       str= index(&str[0],'%');
+    }
+    name= (char *)_XkbAlloc(len+1);
+    str= orig;
+    outstr= name;
+    while (*str!='\0') {
+       if (str[0]=='%') {
+           char pfx,sfx;
+           str++;
+           pfx= str[0];
+           sfx= '\0';
+           if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
+               str++;
+           }
+           else if (pfx=='(') {
+               sfx= ')';
+               str++;
+           }
+           else pfx= '\0';
+
+           var = str;
+           str = get_index(var + 1, &ndx);
+           if (ndx == -1) {
+               continue;
+            }
+           if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
+               if (pfx) *outstr++= pfx;
+               strcpy(outstr,mdefs->layout[ndx]);
+               outstr+= strlen(mdefs->layout[ndx]);
+               if (sfx) *outstr++= sfx;
+           }
+           else if ((*var=='m')&&(mdefs->model)) {
+               if (pfx) *outstr++= pfx;
+               strcpy(outstr,mdefs->model);
+               outstr+= strlen(mdefs->model);
+               if (sfx) *outstr++= sfx;
+           }
+           else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
+               if (pfx) *outstr++= pfx;
+               strcpy(outstr,mdefs->variant[ndx]);
+               outstr+= strlen(mdefs->variant[ndx]);
+               if (sfx) *outstr++= sfx;
+           }
+           if ((pfx=='(')&&(*str==')'))
+               str++;
+       }
+       else {
+           *outstr++= *str++;
+       }
+    }
+    *outstr++= '\0';
+    if (orig!=name)
+       _XkbFree(orig);
+    return name;
+}
+
+/***====================================================================***/
+
+Bool
+XkbcRF_GetComponents(  XkbRF_RulesPtr          rules,
+                       XkbRF_VarDefsPtr        defs,
+                       XkbComponentNamesPtr    names)
+{
+    XkbRF_MultiDefsRec mdefs;
+
+    MakeMultiDefs(&mdefs, defs);
+
+    bzero((char *)names,sizeof(XkbComponentNamesRec));
+    XkbRF_ClearPartialMatches(rules);
+    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
+    XkbRF_ApplyPartialMatches(rules, names);
+    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
+    XkbRF_ApplyPartialMatches(rules, names);
+    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
+    XkbRF_ApplyPartialMatches(rules, names);
+
+    if (names->keycodes)
+       names->keycodes= XkbRF_SubstituteVars(names->keycodes, &mdefs);
+    if (names->symbols)        
+       names->symbols= XkbRF_SubstituteVars(names->symbols, &mdefs);
+    if (names->types)
+       names->types= XkbRF_SubstituteVars(names->types, &mdefs);
+    if (names->compat)
+       names->compat= XkbRF_SubstituteVars(names->compat, &mdefs);
+    if (names->geometry)
+       names->geometry= XkbRF_SubstituteVars(names->geometry, &mdefs);
+    if (names->keymap) 
+       names->keymap= XkbRF_SubstituteVars(names->keymap, &mdefs);
+
+    FreeMultiDefs(&mdefs);
+    return (names->keycodes && names->symbols && names->types &&
+               names->compat && names->geometry ) || names->keymap;
+}
+
+XkbRF_RulePtr
+XkbcRF_AddRule(XkbRF_RulesPtr  rules)
+{
+    if (rules->sz_rules<1) {
+       rules->sz_rules= 16;
+       rules->num_rules= 0;
+       rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
+    }
+    else if (rules->num_rules>=rules->sz_rules) {
+       rules->sz_rules*= 2;
+       rules->rules= _XkbTypedRealloc(rules->rules,rules->sz_rules,
+                                                       XkbRF_RuleRec);
+    }
+    if (!rules->rules) {
+       rules->sz_rules= rules->num_rules= 0;
+#ifdef DEBUG
+       fprintf(stderr,"Allocation failure in XkbcRF_AddRule\n");
+#endif
+       return NULL;
+    }
+    bzero((char *)&rules->rules[rules->num_rules],sizeof(XkbRF_RuleRec));
+    return &rules->rules[rules->num_rules++];
+}
+
+XkbRF_GroupPtr
+XkbcRF_AddGroup(XkbRF_RulesPtr rules)
+{
+    if (rules->sz_groups<1) {
+       rules->sz_groups= 16;
+       rules->num_groups= 0;
+       rules->groups= _XkbTypedCalloc(rules->sz_groups,XkbRF_GroupRec);
+    }
+    else if (rules->num_groups >= rules->sz_groups) {
+       rules->sz_groups *= 2;
+       rules->groups= _XkbTypedRealloc(rules->groups,rules->sz_groups,
+                                                       XkbRF_GroupRec);
+    }
+    if (!rules->groups) {
+       rules->sz_groups= rules->num_groups= 0;
+       return NULL;
+    }
+
+    bzero((char *)&rules->groups[rules->num_groups],sizeof(XkbRF_GroupRec));
+    return &rules->groups[rules->num_groups++];
+}
+
+Bool
+XkbcRF_LoadRules(FILE *file, XkbRF_RulesPtr rules)
+{
+InputLine      line;
+RemapSpec      remap;
+XkbRF_RuleRec  trule,*rule;
+XkbRF_GroupRec  tgroup,*group;
+
+    if (!(rules && file))
+       return False;
+    bzero((char *)&remap,sizeof(RemapSpec));
+    bzero((char *)&tgroup,sizeof(XkbRF_GroupRec));
+    InitInputLine(&line);
+    while (GetInputLine(file,&line,True)) {
+       if (CheckLine(&line,&remap,&trule,&tgroup)) {
+            if (tgroup.number) {
+               if ((group= XkbcRF_AddGroup(rules))!=NULL) {
+                   *group= tgroup;
+                   bzero((char *)&tgroup,sizeof(XkbRF_GroupRec));
+               }
+           } else {
+               if ((rule= XkbcRF_AddRule(rules))!=NULL) {
+                   *rule= trule;
+                   bzero((char *)&trule,sizeof(XkbRF_RuleRec));
+               }
+           }
+       }
+       line.num_line= 0;
+    }
+    FreeInputLine(&line);
+    return True;
+}
+
+Bool
+XkbcRF_LoadRulesByName(char *base,char *locale,XkbRF_RulesPtr rules)
+{
+FILE *         file;
+char           buf[PATH_MAX];
+Bool           ok;
+
+    if ((!base)||(!rules))
+       return False;
+    if (locale) {
+       if (strlen(base)+strlen(locale)+2 > PATH_MAX)
+           return False;
+       sprintf(buf,"%s-%s", base, locale);
+    }
+    else {
+       if (strlen(base)+1 > PATH_MAX)
+           return False;
+       strcpy(buf,base);
+    }
+
+    file= fopen(buf, "r");
+    if ((!file)&&(locale)) { /* fallback if locale was specified */
+       strcpy(buf,base);
+       file= fopen(buf, "r");
+    }
+    if (!file)
+       return False;
+    ok= XkbcRF_LoadRules(file,rules);
+    fclose(file);
+    return ok;
+}
+
+/***====================================================================***/
+
+#define HEAD_NONE      0
+#define HEAD_MODEL     1
+#define HEAD_LAYOUT    2
+#define HEAD_VARIANT   3
+#define HEAD_OPTION    4
+#define        HEAD_EXTRA      5
+
+XkbRF_VarDescPtr
+XkbcRF_AddVarDesc(XkbRF_DescribeVarsPtr        vars)
+{
+    if (vars->sz_desc<1) {
+       vars->sz_desc= 16;
+       vars->num_desc= 0;
+       vars->desc= _XkbTypedCalloc(vars->sz_desc,XkbRF_VarDescRec);
+    }
+    else if (vars->num_desc>=vars->sz_desc) {
+       vars->sz_desc*= 2;
+       vars->desc= _XkbTypedRealloc(vars->desc,vars->sz_desc,XkbRF_VarDescRec);
+    }
+    if (!vars->desc) {
+       vars->sz_desc= vars->num_desc= 0;
+       PR_DEBUG("Allocation failure in XkbcRF_AddVarDesc\n");
+       return NULL;
+    }
+    vars->desc[vars->num_desc].name= NULL;
+    vars->desc[vars->num_desc].desc= NULL;
+    return &vars->desc[vars->num_desc++];
+}
+
+XkbRF_VarDescPtr
+XkbcRF_AddVarDescCopy(XkbRF_DescribeVarsPtr vars,XkbRF_VarDescPtr from)
+{
+XkbRF_VarDescPtr       nd;
+
+    if ((nd=XkbcRF_AddVarDesc(vars))!=NULL) {
+       nd->name= _XkbDupString(from->name);
+       nd->desc= _XkbDupString(from->desc);
+    }
+    return nd;
+}
+
+XkbRF_DescribeVarsPtr 
+XkbcRF_AddVarToDescribe(XkbRF_RulesPtr rules,char *name)
+{
+    if (rules->sz_extra<1) {
+       rules->num_extra= 0;
+       rules->sz_extra= 1;
+       rules->extra_names= _XkbTypedCalloc(rules->sz_extra,char *);
+       rules->extra= _XkbTypedCalloc(rules->sz_extra, XkbRF_DescribeVarsRec);
+    }
+    else if (rules->num_extra>=rules->sz_extra) {
+       rules->sz_extra*= 2;
+       rules->extra_names= _XkbTypedRealloc(rules->extra_names,rules->sz_extra,
+                                                               char *);
+       rules->extra=_XkbTypedRealloc(rules->extra, rules->sz_extra,
+                                                       XkbRF_DescribeVarsRec);
+    }
+    if ((!rules->extra_names)||(!rules->extra)) {
+       PR_DEBUG("allocation error in extra parts\n");
+       rules->sz_extra= rules->num_extra= 0;
+       rules->extra_names= NULL;
+       rules->extra= NULL;
+       return NULL;
+    }
+    rules->extra_names[rules->num_extra]= _XkbDupString(name);
+    bzero(&rules->extra[rules->num_extra],sizeof(XkbRF_DescribeVarsRec));
+    return &rules->extra[rules->num_extra++];
+}
+
+Bool
+XkbcRF_LoadDescriptions(FILE *file,XkbRF_RulesPtr rules)
+{
+InputLine              line;
+XkbRF_VarDescRec       tmp;
+char                   *tok;
+int                    len,headingtype,extra_ndx = 0;
+
+    bzero((char *)&tmp, sizeof(XkbRF_VarDescRec));
+    headingtype = HEAD_NONE;
+    InitInputLine(&line);
+    for ( ; GetInputLine(file,&line,False); line.num_line= 0) {
+       if (line.line[0]=='!') {
+           tok = strtok(&(line.line[1]), " \t");
+           if (strcmp(tok,"model") == 0)
+               headingtype = HEAD_MODEL;
+           else if (_XkbStrCaseCmp(tok,"layout") == 0)
+               headingtype = HEAD_LAYOUT;
+           else if (_XkbStrCaseCmp(tok,"variant") == 0)
+               headingtype = HEAD_VARIANT;
+           else if (_XkbStrCaseCmp(tok,"option") == 0)
+               headingtype = HEAD_OPTION;
+           else {
+               int i;
+               headingtype = HEAD_EXTRA;
+               extra_ndx= -1;
+               for (i=0;(i<rules->num_extra)&&(extra_ndx<0);i++) {
+                   if (_XkbStrCaseCmp(tok,rules->extra_names[i]))
+                       extra_ndx= i;
+               }
+               if (extra_ndx<0) {
+                   XkbRF_DescribeVarsPtr       var;
+                   PR_DEBUG1("Extra heading \"%s\" encountered\n",tok);
+                   var= XkbcRF_AddVarToDescribe(rules,tok);
+                   if (var)
+                        extra_ndx= var-rules->extra;
+                   else headingtype= HEAD_NONE;
+               }
+           }
+           continue;
+       }
+
+       if (headingtype == HEAD_NONE) {
+           PR_DEBUG("Must have a heading before first line of data\n");
+           PR_DEBUG("Illegal line of data ignored\n");
+           continue;
+       }
+
+       len = strlen(line.line);
+       if ((tmp.name= strtok(line.line, " \t")) == NULL) {
+           PR_DEBUG("Huh? No token on line\n");
+           PR_DEBUG("Illegal line of data ignored\n");
+           continue;
+       }
+       if (strlen(tmp.name) == len) {
+           PR_DEBUG("No description found\n");
+           PR_DEBUG("Illegal line of data ignored\n");
+           continue;
+       }
+
+       tok = line.line + strlen(tmp.name) + 1;
+       while ((*tok!='\n')&&isspace(*tok))
+               tok++;
+       if (*tok == '\0') {
+           PR_DEBUG("No description found\n");
+           PR_DEBUG("Illegal line of data ignored\n");
+           continue;
+       }
+       tmp.desc= tok;
+       switch (headingtype) {
+           case HEAD_MODEL:
+               XkbcRF_AddVarDescCopy(&rules->models,&tmp);
+               break;
+           case HEAD_LAYOUT:
+               XkbcRF_AddVarDescCopy(&rules->layouts,&tmp);
+               break;
+           case HEAD_VARIANT:
+               XkbcRF_AddVarDescCopy(&rules->variants,&tmp);
+               break;
+           case HEAD_OPTION:
+               XkbcRF_AddVarDescCopy(&rules->options,&tmp);
+               break;
+           case HEAD_EXTRA:
+               XkbcRF_AddVarDescCopy(&rules->extra[extra_ndx],&tmp);
+               break;
+       }
+    }
+    FreeInputLine(&line);
+    if ((rules->models.num_desc==0) && (rules->layouts.num_desc==0) &&
+       (rules->variants.num_desc==0) && (rules->options.num_desc==0) &&
+       (rules->num_extra==0)) {
+       return False;
+    }
+    return True;
+}
+
+Bool
+XkbcRF_LoadDescriptionsByName(char *base,char *locale,XkbRF_RulesPtr rules)
+{
+FILE *         file;
+char           buf[PATH_MAX];
+Bool           ok;
+
+    if ((!base)||(!rules))
+       return False;
+    if (locale) {
+       if (strlen(base)+strlen(locale)+6 > PATH_MAX)
+           return False;
+       sprintf(buf,"%s-%s.lst", base, locale);
+    }
+    else {
+       if (strlen(base)+5 > PATH_MAX)
+           return False;
+       sprintf(buf,"%s.lst", base);
+    }
+
+    file= fopen(buf, "r");
+    if ((!file)&&(locale)) { /* fallback if locale was specified */
+       sprintf(buf,"%s.lst", base);
+
+       file= fopen(buf, "r");
+    }
+    if (!file)
+       return False;
+    ok= XkbcRF_LoadDescriptions(file,rules);
+    fclose(file);
+    return ok;
+}
+
+/***====================================================================***/
+
+XkbRF_RulesPtr
+XkbcRF_Load(char *base,char *locale,Bool wantDesc,Bool wantRules)
+{
+XkbRF_RulesPtr rules;
+
+    if ((!base)||((!wantDesc)&&(!wantRules)))
+       return NULL;
+    if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
+       return NULL;
+    if (wantDesc&&(!XkbcRF_LoadDescriptionsByName(base,locale,rules))) {
+       XkbcRF_Free(rules,True);
+       return NULL;
+    }
+    if (wantRules&&(!XkbcRF_LoadRulesByName(base,locale,rules))) {
+       XkbcRF_Free(rules,True);
+       return NULL;
+    }
+    return rules;
+}
+
+XkbRF_RulesPtr
+XkbcRF_Create(int szRules,int szExtra) 
+{
+XkbRF_RulesPtr rules;
+
+    if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
+       return NULL;
+    if (szRules>0) {
+       rules->sz_rules= szRules; 
+       rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
+       if (!rules->rules) {
+           _XkbFree(rules);
+           return NULL;
+       }
+    }
+    if (szExtra>0) {
+       rules->sz_extra= szExtra; 
+       rules->extra= _XkbTypedCalloc(rules->sz_extra,XkbRF_DescribeVarsRec);
+       if (!rules->extra) {
+           if (rules->rules)
+               _XkbFree(rules->rules);
+           _XkbFree(rules);
+           return NULL;
+       }
+    }
+    return rules;
+}
+
+/***====================================================================***/
+
+static void
+XkbRF_ClearVarDescriptions(XkbRF_DescribeVarsPtr var)
+{
+register int i;
+    
+    for (i=0;i<var->num_desc;i++) {
+       if (var->desc[i].name)
+           _XkbFree(var->desc[i].name);
+       if (var->desc[i].desc)
+           _XkbFree(var->desc[i].desc);
+       var->desc[i].name= var->desc[i].desc= NULL;
+    }
+    if (var->desc)
+       _XkbFree(var->desc);
+    var->desc= NULL;
+    return;
+}
+
+void
+XkbcRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
+{
+int            i;
+XkbRF_RulePtr  rule;
+XkbRF_GroupPtr group;
+
+    if (!rules)
+       return;
+    XkbRF_ClearVarDescriptions(&rules->models);
+    XkbRF_ClearVarDescriptions(&rules->layouts);
+    XkbRF_ClearVarDescriptions(&rules->variants);
+    XkbRF_ClearVarDescriptions(&rules->options);
+    if (rules->extra) {
+       for (i = 0; i < rules->num_extra; i++) {
+           XkbRF_ClearVarDescriptions(&rules->extra[i]);
+       }
+       _XkbFree(rules->extra);
+       rules->num_extra= rules->sz_extra= 0;
+       rules->extra= NULL;
+    }
+    if (rules->rules) {
+       for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
+           if (rule->model)    _XkbFree(rule->model);
+           if (rule->layout)   _XkbFree(rule->layout);
+           if (rule->variant)  _XkbFree(rule->variant);
+           if (rule->option)   _XkbFree(rule->option);
+           if (rule->keycodes) _XkbFree(rule->keycodes);
+           if (rule->symbols)  _XkbFree(rule->symbols);
+           if (rule->types)    _XkbFree(rule->types);
+           if (rule->compat)   _XkbFree(rule->compat);
+           if (rule->geometry) _XkbFree(rule->geometry);
+           if (rule->keymap)   _XkbFree(rule->keymap);
+           bzero((char *)rule,sizeof(XkbRF_RuleRec));
+       }
+       _XkbFree(rules->rules);
+       rules->num_rules= rules->sz_rules= 0;
+       rules->rules= NULL;
+    }
+
+    if (rules->groups) {
+       for (i=0, group=rules->groups;i<rules->num_groups;i++,group++) {
+           if (group->name)    _XkbFree(group->name);
+           if (group->words)   _XkbFree(group->words);
+       }
+       _XkbFree(rules->groups);
+       rules->num_groups= 0;
+       rules->groups= NULL;
+    }
+    if (freeRules)
+       _XkbFree(rules);
+    return;
+}