maprules.c revision 05b261ec
105b261ecSmrg/************************************************************
205b261ecSmrg Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
305b261ecSmrg
405b261ecSmrg Permission to use, copy, modify, and distribute this
505b261ecSmrg software and its documentation for any purpose and without
605b261ecSmrg fee is hereby granted, provided that the above copyright
705b261ecSmrg notice appear in all copies and that both that copyright
805b261ecSmrg notice and this permission notice appear in supporting
905b261ecSmrg documentation, and that the name of Silicon Graphics not be
1005b261ecSmrg used in advertising or publicity pertaining to distribution
1105b261ecSmrg of the software without specific prior written permission.
1205b261ecSmrg Silicon Graphics makes no representation about the suitability
1305b261ecSmrg of this software for any purpose. It is provided "as is"
1405b261ecSmrg without any express or implied warranty.
1505b261ecSmrg
1605b261ecSmrg SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
1705b261ecSmrg SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1805b261ecSmrg AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
1905b261ecSmrg GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
2005b261ecSmrg DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
2105b261ecSmrg DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
2205b261ecSmrg OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
2305b261ecSmrg THE USE OR PERFORMANCE OF THIS SOFTWARE.
2405b261ecSmrg
2505b261ecSmrg ********************************************************/
2605b261ecSmrg
2705b261ecSmrg#ifdef HAVE_DIX_CONFIG_H
2805b261ecSmrg#include <dix-config.h>
2905b261ecSmrg#endif
3005b261ecSmrg
3105b261ecSmrg#include <stdio.h>
3205b261ecSmrg#include <ctype.h>
3305b261ecSmrg#include <stdlib.h>
3405b261ecSmrg
3505b261ecSmrg#define X_INCLUDE_STRING_H
3605b261ecSmrg#define XOS_USE_NO_LOCKING
3705b261ecSmrg#include <X11/Xos_r.h>
3805b261ecSmrg
3905b261ecSmrg#define NEED_EVENTS
4005b261ecSmrg#include <X11/Xproto.h>
4105b261ecSmrg#include <X11/X.h>
4205b261ecSmrg#include <X11/Xos.h>
4305b261ecSmrg#include <X11/Xfuncs.h>
4405b261ecSmrg#include <X11/Xatom.h>
4505b261ecSmrg#include <X11/keysym.h>
4605b261ecSmrg#include "misc.h"
4705b261ecSmrg#include "inputstr.h"
4805b261ecSmrg#include "dix.h"
4905b261ecSmrg#include <X11/extensions/XKBstr.h>
5005b261ecSmrg#define XKBSRV_NEED_FILE_FUNCS
5105b261ecSmrg#include <xkbsrv.h>
5205b261ecSmrg
5305b261ecSmrg#ifdef DEBUG
5405b261ecSmrg#define PR_DEBUG(s)		fprintf(stderr,s)
5505b261ecSmrg#define PR_DEBUG1(s,a)		fprintf(stderr,s,a)
5605b261ecSmrg#define PR_DEBUG2(s,a,b)	fprintf(stderr,s,a,b)
5705b261ecSmrg#else
5805b261ecSmrg#define PR_DEBUG(s)
5905b261ecSmrg#define PR_DEBUG1(s,a)
6005b261ecSmrg#define PR_DEBUG2(s,a,b)
6105b261ecSmrg#endif
6205b261ecSmrg
6305b261ecSmrg/***====================================================================***/
6405b261ecSmrg
6505b261ecSmrg#define DFLT_LINE_SIZE	128
6605b261ecSmrg
6705b261ecSmrgtypedef struct {
6805b261ecSmrg	int	line_num;
6905b261ecSmrg	int	sz_line;
7005b261ecSmrg	int	num_line;
7105b261ecSmrg	char	buf[DFLT_LINE_SIZE];
7205b261ecSmrg	char *	line;
7305b261ecSmrg} InputLine;
7405b261ecSmrg
7505b261ecSmrgstatic void
7605b261ecSmrgInitInputLine(InputLine *line)
7705b261ecSmrg{
7805b261ecSmrg    line->line_num= 1;
7905b261ecSmrg    line->num_line= 0;
8005b261ecSmrg    line->sz_line= DFLT_LINE_SIZE;
8105b261ecSmrg    line->line=	line->buf;
8205b261ecSmrg    return;
8305b261ecSmrg}
8405b261ecSmrg
8505b261ecSmrgstatic void
8605b261ecSmrgFreeInputLine(InputLine *line)
8705b261ecSmrg{
8805b261ecSmrg    if (line->line!=line->buf)
8905b261ecSmrg	_XkbFree(line->line);
9005b261ecSmrg    line->line_num= 1;
9105b261ecSmrg    line->num_line= 0;
9205b261ecSmrg    line->sz_line= DFLT_LINE_SIZE;
9305b261ecSmrg    line->line= line->buf;
9405b261ecSmrg    return;
9505b261ecSmrg}
9605b261ecSmrg
9705b261ecSmrgstatic int
9805b261ecSmrgInputLineAddChar(InputLine *line,int ch)
9905b261ecSmrg{
10005b261ecSmrg    if (line->num_line>=line->sz_line) {
10105b261ecSmrg	if (line->line==line->buf) {
10205b261ecSmrg	    line->line= (char *)_XkbAlloc(line->sz_line*2);
10305b261ecSmrg	    memcpy(line->line,line->buf,line->sz_line);
10405b261ecSmrg	}
10505b261ecSmrg	else {
10605b261ecSmrg	    line->line=(char *)_XkbRealloc((char *)line->line,line->sz_line*2);
10705b261ecSmrg	}
10805b261ecSmrg	line->sz_line*= 2;
10905b261ecSmrg    }
11005b261ecSmrg    line->line[line->num_line++]= ch;
11105b261ecSmrg    return ch;
11205b261ecSmrg}
11305b261ecSmrg
11405b261ecSmrg#define	ADD_CHAR(l,c)	((l)->num_line<(l)->sz_line?\
11505b261ecSmrg				(int)((l)->line[(l)->num_line++]= (c)):\
11605b261ecSmrg				InputLineAddChar(l,c))
11705b261ecSmrg
11805b261ecSmrgstatic Bool
11905b261ecSmrgGetInputLine(FILE *file,InputLine *line,Bool checkbang)
12005b261ecSmrg{
12105b261ecSmrgint	ch;
12205b261ecSmrgBool	endOfFile,spacePending,slashPending,inComment;
12305b261ecSmrg
12405b261ecSmrg     endOfFile= False;
12505b261ecSmrg     while ((!endOfFile)&&(line->num_line==0)) {
12605b261ecSmrg	spacePending= slashPending= inComment= False;
12705b261ecSmrg	while (((ch=getc(file))!='\n')&&(ch!=EOF)) {
12805b261ecSmrg	    if (ch=='\\') {
12905b261ecSmrg		if ((ch=getc(file))==EOF)
13005b261ecSmrg		    break;
13105b261ecSmrg		if (ch=='\n') {
13205b261ecSmrg		    inComment= False;
13305b261ecSmrg		    ch= ' ';
13405b261ecSmrg		    line->line_num++;
13505b261ecSmrg		}
13605b261ecSmrg	    }
13705b261ecSmrg	    if (inComment)
13805b261ecSmrg		continue;
13905b261ecSmrg	    if (ch=='/') {
14005b261ecSmrg		if (slashPending) {
14105b261ecSmrg		    inComment= True;
14205b261ecSmrg		    slashPending= False;
14305b261ecSmrg		}
14405b261ecSmrg		else {
14505b261ecSmrg		    slashPending= True;
14605b261ecSmrg		}
14705b261ecSmrg		continue;
14805b261ecSmrg	    }
14905b261ecSmrg	    else if (slashPending) {
15005b261ecSmrg		if (spacePending) {
15105b261ecSmrg		    ADD_CHAR(line,' ');
15205b261ecSmrg		    spacePending= False;
15305b261ecSmrg		}
15405b261ecSmrg		ADD_CHAR(line,'/');
15505b261ecSmrg		slashPending= False;
15605b261ecSmrg	    }
15705b261ecSmrg	    if (isspace(ch)) {
15805b261ecSmrg		while (isspace(ch)&&(ch!='\n')&&(ch!=EOF)) {
15905b261ecSmrg		    ch= getc(file);
16005b261ecSmrg		}
16105b261ecSmrg		if (ch==EOF)
16205b261ecSmrg		    break;
16305b261ecSmrg		if ((ch!='\n')&&(line->num_line>0))
16405b261ecSmrg		    spacePending= True;
16505b261ecSmrg		ungetc(ch,file);
16605b261ecSmrg	    }
16705b261ecSmrg	    else {
16805b261ecSmrg		if (spacePending) {
16905b261ecSmrg		    ADD_CHAR(line,' ');
17005b261ecSmrg		    spacePending= False;
17105b261ecSmrg		}
17205b261ecSmrg		if (checkbang && ch=='!') {
17305b261ecSmrg		    if (line->num_line!=0) {
17405b261ecSmrg			PR_DEBUG("The '!' legal only at start of line\n");
17505b261ecSmrg			PR_DEBUG("Line containing '!' ignored\n");
17605b261ecSmrg			line->num_line= 0;
17705b261ecSmrg			inComment= 0;
17805b261ecSmrg			break;
17905b261ecSmrg		    }
18005b261ecSmrg
18105b261ecSmrg		}
18205b261ecSmrg		ADD_CHAR(line,ch);
18305b261ecSmrg	    }
18405b261ecSmrg	}
18505b261ecSmrg	if (ch==EOF)
18605b261ecSmrg	     endOfFile= True;
18705b261ecSmrg/*	else line->num_line++;*/
18805b261ecSmrg     }
18905b261ecSmrg     if ((line->num_line==0)&&(endOfFile))
19005b261ecSmrg	return False;
19105b261ecSmrg      ADD_CHAR(line,'\0');
19205b261ecSmrg      return True;
19305b261ecSmrg}
19405b261ecSmrg
19505b261ecSmrg/***====================================================================***/
19605b261ecSmrg
19705b261ecSmrg#define	MODEL		0
19805b261ecSmrg#define	LAYOUT		1
19905b261ecSmrg#define	VARIANT		2
20005b261ecSmrg#define	OPTION		3
20105b261ecSmrg#define	KEYCODES	4
20205b261ecSmrg#define SYMBOLS		5
20305b261ecSmrg#define	TYPES		6
20405b261ecSmrg#define	COMPAT		7
20505b261ecSmrg#define	GEOMETRY	8
20605b261ecSmrg#define	KEYMAP		9
20705b261ecSmrg#define	MAX_WORDS	10
20805b261ecSmrg
20905b261ecSmrg#define	PART_MASK	0x000F
21005b261ecSmrg#define	COMPONENT_MASK	0x03F0
21105b261ecSmrg
21205b261ecSmrgstatic	char *	cname[MAX_WORDS] = {
21305b261ecSmrg	"model", "layout", "variant", "option",
21405b261ecSmrg	"keycodes", "symbols", "types", "compat", "geometry", "keymap"
21505b261ecSmrg};
21605b261ecSmrg
21705b261ecSmrgtypedef	struct _RemapSpec {
21805b261ecSmrg	int			number;
21905b261ecSmrg	int			num_remap;
22005b261ecSmrg	struct	{
22105b261ecSmrg		int	word;
22205b261ecSmrg		int	index;
22305b261ecSmrg                }		remap[MAX_WORDS];
22405b261ecSmrg} RemapSpec;
22505b261ecSmrg
22605b261ecSmrgtypedef struct _FileSpec {
22705b261ecSmrg	char *			name[MAX_WORDS];
22805b261ecSmrg	struct _FileSpec *	pending;
22905b261ecSmrg} FileSpec;
23005b261ecSmrg
23105b261ecSmrgtypedef struct {
23205b261ecSmrg	char *			model;
23305b261ecSmrg	char *			layout[XkbNumKbdGroups+1];
23405b261ecSmrg	char *			variant[XkbNumKbdGroups+1];
23505b261ecSmrg	char *			options;
23605b261ecSmrg} XkbRF_MultiDefsRec, *XkbRF_MultiDefsPtr;
23705b261ecSmrg
23805b261ecSmrg#define NDX_BUFF_SIZE	4
23905b261ecSmrg
24005b261ecSmrg/***====================================================================***/
24105b261ecSmrg
24205b261ecSmrgstatic char*
24305b261ecSmrgget_index(char *str, int *ndx)
24405b261ecSmrg{
24505b261ecSmrg   char ndx_buf[NDX_BUFF_SIZE];
24605b261ecSmrg   char *end;
24705b261ecSmrg
24805b261ecSmrg   if (*str != '[') {
24905b261ecSmrg       *ndx = 0;
25005b261ecSmrg       return str;
25105b261ecSmrg   }
25205b261ecSmrg   str++;
25305b261ecSmrg   end = strchr(str, ']');
25405b261ecSmrg   if (end == NULL) {
25505b261ecSmrg       *ndx = -1;
25605b261ecSmrg       return str - 1;
25705b261ecSmrg   }
25805b261ecSmrg   if ( (end - str) >= NDX_BUFF_SIZE) {
25905b261ecSmrg       *ndx = -1;
26005b261ecSmrg       return end + 1;
26105b261ecSmrg   }
26205b261ecSmrg   strncpy(ndx_buf, str, end - str);
26305b261ecSmrg   ndx_buf[end - str] = '\0';
26405b261ecSmrg   *ndx = atoi(ndx_buf);
26505b261ecSmrg   return end + 1;
26605b261ecSmrg}
26705b261ecSmrg
26805b261ecSmrgstatic void
26905b261ecSmrgSetUpRemap(InputLine *line,RemapSpec *remap)
27005b261ecSmrg{
27105b261ecSmrgchar *		tok,*str;
27205b261ecSmrgunsigned	present, l_ndx_present, v_ndx_present;
27305b261ecSmrgregister int	i;
27405b261ecSmrgint		len, ndx;
27505b261ecSmrg_Xstrtokparams	strtok_buf;
27605b261ecSmrg#ifdef DEBUG
27705b261ecSmrgBool		found;
27805b261ecSmrg#endif
27905b261ecSmrg
28005b261ecSmrg
28105b261ecSmrg   l_ndx_present = v_ndx_present = present= 0;
28205b261ecSmrg   str= &line->line[1];
28305b261ecSmrg   len = remap->number;
28405b261ecSmrg   bzero((char *)remap,sizeof(RemapSpec));
28505b261ecSmrg   remap->number = len;
28605b261ecSmrg   while ((tok=_XStrtok(str," ",strtok_buf))!=NULL) {
28705b261ecSmrg#ifdef DEBUG
28805b261ecSmrg	found= False;
28905b261ecSmrg#endif
29005b261ecSmrg	str= NULL;
29105b261ecSmrg	if (strcmp(tok,"=")==0)
29205b261ecSmrg	    continue;
29305b261ecSmrg	for (i=0;i<MAX_WORDS;i++) {
29405b261ecSmrg            len = strlen(cname[i]);
29505b261ecSmrg	    if (strncmp(cname[i],tok,len)==0) {
29605b261ecSmrg		if(strlen(tok) > len) {
29705b261ecSmrg		    char *end = get_index(tok+len, &ndx);
29805b261ecSmrg		    if ((i != LAYOUT && i != VARIANT) ||
29905b261ecSmrg			*end != '\0' || ndx == -1)
30005b261ecSmrg		        break;
30105b261ecSmrg		     if (ndx < 1 || ndx > XkbNumKbdGroups) {
30205b261ecSmrg		        PR_DEBUG2("Illegal %s index: %d\n", cname[i], ndx);
30305b261ecSmrg		        PR_DEBUG1("Index must be in range 1..%d\n",
30405b261ecSmrg				   XkbNumKbdGroups);
30505b261ecSmrg			break;
30605b261ecSmrg		     }
30705b261ecSmrg                } else {
30805b261ecSmrg		    ndx = 0;
30905b261ecSmrg                }
31005b261ecSmrg#ifdef DEBUG
31105b261ecSmrg		found= True;
31205b261ecSmrg#endif
31305b261ecSmrg		if (present&(1<<i)) {
31405b261ecSmrg		    if ((i == LAYOUT && l_ndx_present&(1<<ndx)) ||
31505b261ecSmrg			(i == VARIANT && v_ndx_present&(1<<ndx)) ) {
31605b261ecSmrg		        PR_DEBUG1("Component \"%s\" listed twice\n",tok);
31705b261ecSmrg		        PR_DEBUG("Second definition ignored\n");
31805b261ecSmrg		        break;
31905b261ecSmrg		    }
32005b261ecSmrg		}
32105b261ecSmrg		present |= (1<<i);
32205b261ecSmrg                if (i == LAYOUT)
32305b261ecSmrg                    l_ndx_present |= 1 << ndx;
32405b261ecSmrg                if (i == VARIANT)
32505b261ecSmrg                    v_ndx_present |= 1 << ndx;
32605b261ecSmrg		remap->remap[remap->num_remap].word= i;
32705b261ecSmrg		remap->remap[remap->num_remap++].index= ndx;
32805b261ecSmrg		break;
32905b261ecSmrg	    }
33005b261ecSmrg	}
33105b261ecSmrg#ifdef DEBUG
33205b261ecSmrg	if (!found) {
33305b261ecSmrg	    fprintf(stderr,"Unknown component \"%s\" ignored\n",tok);
33405b261ecSmrg	}
33505b261ecSmrg#endif
33605b261ecSmrg   }
33705b261ecSmrg   if ((present&PART_MASK)==0) {
33805b261ecSmrg#ifdef DEBUG
33905b261ecSmrg	unsigned mask= PART_MASK;
34005b261ecSmrg	fprintf(stderr,"Mapping needs at least one of ");
34105b261ecSmrg	for (i=0; (i<MAX_WORDS); i++) {
34205b261ecSmrg	    if ((1L<<i)&mask) {
34305b261ecSmrg		mask&= ~(1L<<i);
34405b261ecSmrg		if (mask)	fprintf(stderr,"\"%s,\" ",cname[i]);
34505b261ecSmrg		else		fprintf(stderr,"or \"%s\"\n",cname[i]);
34605b261ecSmrg	    }
34705b261ecSmrg	}
34805b261ecSmrg	fprintf(stderr,"Illegal mapping ignored\n");
34905b261ecSmrg#endif
35005b261ecSmrg	remap->num_remap= 0;
35105b261ecSmrg	return;
35205b261ecSmrg   }
35305b261ecSmrg   if ((present&COMPONENT_MASK)==0) {
35405b261ecSmrg	PR_DEBUG("Mapping needs at least one component\n");
35505b261ecSmrg	PR_DEBUG("Illegal mapping ignored\n");
35605b261ecSmrg	remap->num_remap= 0;
35705b261ecSmrg	return;
35805b261ecSmrg   }
35905b261ecSmrg   if (((present&COMPONENT_MASK)&(1<<KEYMAP))&&
36005b261ecSmrg				((present&COMPONENT_MASK)!=(1<<KEYMAP))) {
36105b261ecSmrg	PR_DEBUG("Keymap cannot appear with other components\n");
36205b261ecSmrg	PR_DEBUG("Illegal mapping ignored\n");
36305b261ecSmrg	remap->num_remap= 0;
36405b261ecSmrg	return;
36505b261ecSmrg   }
36605b261ecSmrg   remap->number++;
36705b261ecSmrg   return;
36805b261ecSmrg}
36905b261ecSmrg
37005b261ecSmrgstatic Bool
37105b261ecSmrgMatchOneOf(char *wanted,char *vals_defined)
37205b261ecSmrg{
37305b261ecSmrgchar	*str,*next;
37405b261ecSmrgint	want_len= strlen(wanted);
37505b261ecSmrg
37605b261ecSmrg    for (str=vals_defined,next=NULL;str!=NULL;str=next) {
37705b261ecSmrg	int len;
37805b261ecSmrg	next= strchr(str,',');
37905b261ecSmrg	if (next) {
38005b261ecSmrg	    len= next-str;
38105b261ecSmrg	    next++;
38205b261ecSmrg	}
38305b261ecSmrg	else {
38405b261ecSmrg	    len= strlen(str);
38505b261ecSmrg	}
38605b261ecSmrg	if ((len==want_len)&&(strncmp(wanted,str,len)==0))
38705b261ecSmrg	    return True;
38805b261ecSmrg    }
38905b261ecSmrg    return False;
39005b261ecSmrg}
39105b261ecSmrg
39205b261ecSmrg/***====================================================================***/
39305b261ecSmrg
39405b261ecSmrgstatic Bool
39505b261ecSmrgCheckLine(	InputLine *		line,
39605b261ecSmrg		RemapSpec *		remap,
39705b261ecSmrg		XkbRF_RulePtr		rule,
39805b261ecSmrg		XkbRF_GroupPtr		group)
39905b261ecSmrg{
40005b261ecSmrgchar *		str,*tok;
40105b261ecSmrgregister int	nread, i;
40205b261ecSmrgFileSpec	tmp;
40305b261ecSmrg_Xstrtokparams	strtok_buf;
40405b261ecSmrgBool 		append = False;
40505b261ecSmrg
40605b261ecSmrg    if (line->line[0]=='!') {
40705b261ecSmrg        if (line->line[1] == '$' ||
40805b261ecSmrg            (line->line[1] == ' ' && line->line[2] == '$')) {
40905b261ecSmrg            char *gname = strchr(line->line, '$');
41005b261ecSmrg            char *words = strchr(gname, ' ');
41105b261ecSmrg            if(!words)
41205b261ecSmrg                return False;
41305b261ecSmrg            *words++ = '\0';
41405b261ecSmrg            for (; *words; words++) {
41505b261ecSmrg                if (*words != '=' && *words != ' ')
41605b261ecSmrg                    break;
41705b261ecSmrg            }
41805b261ecSmrg            if (*words == '\0')
41905b261ecSmrg                return False;
42005b261ecSmrg            group->name = _XkbDupString(gname);
42105b261ecSmrg            group->words = _XkbDupString(words);
42205b261ecSmrg            for (i = 1, words = group->words; *words; words++) {
42305b261ecSmrg                 if ( *words == ' ') {
42405b261ecSmrg                     *words++ = '\0';
42505b261ecSmrg                     i++;
42605b261ecSmrg                 }
42705b261ecSmrg            }
42805b261ecSmrg            group->number = i;
42905b261ecSmrg            return True;
43005b261ecSmrg        } else {
43105b261ecSmrg	    SetUpRemap(line,remap);
43205b261ecSmrg	    return False;
43305b261ecSmrg        }
43405b261ecSmrg    }
43505b261ecSmrg
43605b261ecSmrg    if (remap->num_remap==0) {
43705b261ecSmrg	PR_DEBUG("Must have a mapping before first line of data\n");
43805b261ecSmrg	PR_DEBUG("Illegal line of data ignored\n");
43905b261ecSmrg	return False;
44005b261ecSmrg    }
44105b261ecSmrg    bzero((char *)&tmp,sizeof(FileSpec));
44205b261ecSmrg    str= line->line;
44305b261ecSmrg    for (nread= 0;(tok=_XStrtok(str," ",strtok_buf))!=NULL;nread++) {
44405b261ecSmrg	str= NULL;
44505b261ecSmrg	if (strcmp(tok,"=")==0) {
44605b261ecSmrg	    nread--;
44705b261ecSmrg	    continue;
44805b261ecSmrg	}
44905b261ecSmrg	if (nread>remap->num_remap) {
45005b261ecSmrg	    PR_DEBUG("Too many words on a line\n");
45105b261ecSmrg	    PR_DEBUG1("Extra word \"%s\" ignored\n",tok);
45205b261ecSmrg	    continue;
45305b261ecSmrg	}
45405b261ecSmrg	tmp.name[remap->remap[nread].word]= tok;
45505b261ecSmrg	if (*tok == '+' || *tok == '|')
45605b261ecSmrg	    append = True;
45705b261ecSmrg    }
45805b261ecSmrg    if (nread<remap->num_remap) {
45905b261ecSmrg	PR_DEBUG1("Too few words on a line: %s\n", line->line);
46005b261ecSmrg	PR_DEBUG("line ignored\n");
46105b261ecSmrg	return False;
46205b261ecSmrg    }
46305b261ecSmrg
46405b261ecSmrg    rule->flags= 0;
46505b261ecSmrg    rule->number = remap->number;
46605b261ecSmrg    if (tmp.name[OPTION])
46705b261ecSmrg	 rule->flags|= XkbRF_Option;
46805b261ecSmrg    else if (append)
46905b261ecSmrg	 rule->flags|= XkbRF_Append;
47005b261ecSmrg    else
47105b261ecSmrg	 rule->flags|= XkbRF_Normal;
47205b261ecSmrg    rule->model= _XkbDupString(tmp.name[MODEL]);
47305b261ecSmrg    rule->layout= _XkbDupString(tmp.name[LAYOUT]);
47405b261ecSmrg    rule->variant= _XkbDupString(tmp.name[VARIANT]);
47505b261ecSmrg    rule->option= _XkbDupString(tmp.name[OPTION]);
47605b261ecSmrg
47705b261ecSmrg    rule->keycodes= _XkbDupString(tmp.name[KEYCODES]);
47805b261ecSmrg    rule->symbols= _XkbDupString(tmp.name[SYMBOLS]);
47905b261ecSmrg    rule->types= _XkbDupString(tmp.name[TYPES]);
48005b261ecSmrg    rule->compat= _XkbDupString(tmp.name[COMPAT]);
48105b261ecSmrg    rule->geometry= _XkbDupString(tmp.name[GEOMETRY]);
48205b261ecSmrg    rule->keymap= _XkbDupString(tmp.name[KEYMAP]);
48305b261ecSmrg
48405b261ecSmrg    rule->layout_num = rule->variant_num = 0;
48505b261ecSmrg    for (i = 0; i < nread; i++) {
48605b261ecSmrg        if (remap->remap[i].index) {
48705b261ecSmrg	    if (remap->remap[i].word == LAYOUT)
48805b261ecSmrg	        rule->layout_num = remap->remap[i].index;
48905b261ecSmrg	    if (remap->remap[i].word == VARIANT)
49005b261ecSmrg	        rule->variant_num = remap->remap[i].index;
49105b261ecSmrg        }
49205b261ecSmrg    }
49305b261ecSmrg    return True;
49405b261ecSmrg}
49505b261ecSmrg
49605b261ecSmrgstatic char *
49705b261ecSmrg_Concat(char *str1,char *str2)
49805b261ecSmrg{
49905b261ecSmrgint len;
50005b261ecSmrg
50105b261ecSmrg    if ((!str1)||(!str2))
50205b261ecSmrg	return str1;
50305b261ecSmrg    len= strlen(str1)+strlen(str2)+1;
50405b261ecSmrg    str1= _XkbTypedRealloc(str1,len,char);
50505b261ecSmrg    if (str1)
50605b261ecSmrg	strcat(str1,str2);
50705b261ecSmrg    return str1;
50805b261ecSmrg}
50905b261ecSmrg
51005b261ecSmrgstatic void
51105b261ecSmrgsqueeze_spaces(char *p1)
51205b261ecSmrg{
51305b261ecSmrg   char *p2;
51405b261ecSmrg   for (p2 = p1; *p2; p2++) {
51505b261ecSmrg       *p1 = *p2;
51605b261ecSmrg       if (*p1 != ' ') p1++;
51705b261ecSmrg   }
51805b261ecSmrg   *p1 = '\0';
51905b261ecSmrg}
52005b261ecSmrg
52105b261ecSmrgstatic Bool
52205b261ecSmrgMakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs)
52305b261ecSmrg{
52405b261ecSmrg
52505b261ecSmrg   bzero((char *)mdefs,sizeof(XkbRF_MultiDefsRec));
52605b261ecSmrg   mdefs->model = defs->model;
52705b261ecSmrg   mdefs->options = _XkbDupString(defs->options);
52805b261ecSmrg   if (mdefs->options) squeeze_spaces(mdefs->options);
52905b261ecSmrg
53005b261ecSmrg   if (defs->layout) {
53105b261ecSmrg       if (!strchr(defs->layout, ',')) {
53205b261ecSmrg           mdefs->layout[0] = defs->layout;
53305b261ecSmrg       } else {
53405b261ecSmrg           char *p;
53505b261ecSmrg           int i;
53605b261ecSmrg           mdefs->layout[1] = _XkbDupString(defs->layout);
53705b261ecSmrg	   if (mdefs->layout[1] == NULL)
53805b261ecSmrg	      return False;
53905b261ecSmrg           squeeze_spaces(mdefs->layout[1]);
54005b261ecSmrg           p = mdefs->layout[1];
54105b261ecSmrg           for (i = 2; i <= XkbNumKbdGroups; i++) {
54205b261ecSmrg              if ((p = strchr(p, ','))) {
54305b261ecSmrg                 *p++ = '\0';
54405b261ecSmrg                 mdefs->layout[i] = p;
54505b261ecSmrg              } else {
54605b261ecSmrg                 break;
54705b261ecSmrg              }
54805b261ecSmrg           }
54905b261ecSmrg           if (p && (p = strchr(p, ',')))
55005b261ecSmrg              *p = '\0';
55105b261ecSmrg       }
55205b261ecSmrg   }
55305b261ecSmrg
55405b261ecSmrg   if (defs->variant) {
55505b261ecSmrg       if (!strchr(defs->variant, ',')) {
55605b261ecSmrg           mdefs->variant[0] = defs->variant;
55705b261ecSmrg       } else {
55805b261ecSmrg           char *p;
55905b261ecSmrg           int i;
56005b261ecSmrg           mdefs->variant[1] = _XkbDupString(defs->variant);
56105b261ecSmrg	   if (mdefs->variant[1] == NULL)
56205b261ecSmrg	      return False;
56305b261ecSmrg           squeeze_spaces(mdefs->variant[1]);
56405b261ecSmrg           p = mdefs->variant[1];
56505b261ecSmrg           for (i = 2; i <= XkbNumKbdGroups; i++) {
56605b261ecSmrg              if ((p = strchr(p, ','))) {
56705b261ecSmrg                 *p++ = '\0';
56805b261ecSmrg                 mdefs->variant[i] = p;
56905b261ecSmrg              } else {
57005b261ecSmrg                 break;
57105b261ecSmrg              }
57205b261ecSmrg           }
57305b261ecSmrg           if (p && (p = strchr(p, ',')))
57405b261ecSmrg              *p = '\0';
57505b261ecSmrg       }
57605b261ecSmrg   }
57705b261ecSmrg   return True;
57805b261ecSmrg}
57905b261ecSmrg
58005b261ecSmrgstatic void
58105b261ecSmrgFreeMultiDefs(XkbRF_MultiDefsPtr defs)
58205b261ecSmrg{
58305b261ecSmrg  if (defs->options) _XkbFree(defs->options);
58405b261ecSmrg  if (defs->layout[1])  _XkbFree(defs->layout[1]);
58505b261ecSmrg  if (defs->variant[1])  _XkbFree(defs->variant[1]);
58605b261ecSmrg}
58705b261ecSmrg
58805b261ecSmrgstatic void
58905b261ecSmrgApply(char *src, char **dst)
59005b261ecSmrg{
59105b261ecSmrg    if (src) {
59205b261ecSmrg        if (*src == '+' || *src == '!') {
59305b261ecSmrg	    *dst= _Concat(*dst, src);
59405b261ecSmrg        } else {
59505b261ecSmrg            if (*dst == NULL)
59605b261ecSmrg	        *dst= _XkbDupString(src);
59705b261ecSmrg        }
59805b261ecSmrg    }
59905b261ecSmrg}
60005b261ecSmrg
60105b261ecSmrgstatic void
60205b261ecSmrgXkbRF_ApplyRule(	XkbRF_RulePtr 		rule,
60305b261ecSmrg			XkbComponentNamesPtr	names)
60405b261ecSmrg{
60505b261ecSmrg    rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
60605b261ecSmrg
60705b261ecSmrg    Apply(rule->keycodes, &names->keycodes);
60805b261ecSmrg    Apply(rule->symbols,  &names->symbols);
60905b261ecSmrg    Apply(rule->types,    &names->types);
61005b261ecSmrg    Apply(rule->compat,   &names->compat);
61105b261ecSmrg    Apply(rule->geometry, &names->geometry);
61205b261ecSmrg    Apply(rule->keymap,   &names->keymap);
61305b261ecSmrg}
61405b261ecSmrg
61505b261ecSmrgstatic Bool
61605b261ecSmrgCheckGroup(	XkbRF_RulesPtr          rules,
61705b261ecSmrg		char * 			group_name,
61805b261ecSmrg		char * 			name)
61905b261ecSmrg{
62005b261ecSmrg   int i;
62105b261ecSmrg   char *p;
62205b261ecSmrg   XkbRF_GroupPtr group;
62305b261ecSmrg
62405b261ecSmrg   for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
62505b261ecSmrg       if (! strcmp(group->name, group_name)) {
62605b261ecSmrg           break;
62705b261ecSmrg       }
62805b261ecSmrg   }
62905b261ecSmrg   if (i == rules->num_groups)
63005b261ecSmrg       return False;
63105b261ecSmrg   for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
63205b261ecSmrg       if (! strcmp(p, name)) {
63305b261ecSmrg           return True;
63405b261ecSmrg       }
63505b261ecSmrg   }
63605b261ecSmrg   return False;
63705b261ecSmrg}
63805b261ecSmrg
63905b261ecSmrgstatic int
64005b261ecSmrgXkbRF_CheckApplyRule(	XkbRF_RulePtr 		rule,
64105b261ecSmrg			XkbRF_MultiDefsPtr	mdefs,
64205b261ecSmrg			XkbComponentNamesPtr	names,
64305b261ecSmrg			XkbRF_RulesPtr          rules)
64405b261ecSmrg{
64505b261ecSmrg    Bool pending = False;
64605b261ecSmrg
64705b261ecSmrg    if (rule->model != NULL) {
64805b261ecSmrg        if(mdefs->model == NULL)
64905b261ecSmrg            return 0;
65005b261ecSmrg        if (strcmp(rule->model, "*") == 0) {
65105b261ecSmrg            pending = True;
65205b261ecSmrg        } else {
65305b261ecSmrg            if (rule->model[0] == '$') {
65405b261ecSmrg               if (!CheckGroup(rules, rule->model, mdefs->model))
65505b261ecSmrg                  return 0;
65605b261ecSmrg            } else {
65705b261ecSmrg	       if (strcmp(rule->model, mdefs->model) != 0)
65805b261ecSmrg	          return 0;
65905b261ecSmrg	    }
66005b261ecSmrg	}
66105b261ecSmrg    }
66205b261ecSmrg    if (rule->option != NULL) {
66305b261ecSmrg	if (mdefs->options == NULL)
66405b261ecSmrg	    return 0;
66505b261ecSmrg	if ((!MatchOneOf(rule->option,mdefs->options)))
66605b261ecSmrg	    return 0;
66705b261ecSmrg    }
66805b261ecSmrg
66905b261ecSmrg    if (rule->layout != NULL) {
67005b261ecSmrg	if(mdefs->layout[rule->layout_num] == NULL ||
67105b261ecSmrg	   *mdefs->layout[rule->layout_num] == '\0')
67205b261ecSmrg	    return 0;
67305b261ecSmrg        if (strcmp(rule->layout, "*") == 0) {
67405b261ecSmrg            pending = True;
67505b261ecSmrg        } else {
67605b261ecSmrg            if (rule->layout[0] == '$') {
67705b261ecSmrg               if (!CheckGroup(rules, rule->layout,
67805b261ecSmrg                               mdefs->layout[rule->layout_num]))
67905b261ecSmrg                  return 0;
68005b261ecSmrg	    } else {
68105b261ecSmrg	       if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
68205b261ecSmrg	           return 0;
68305b261ecSmrg	    }
68405b261ecSmrg	}
68505b261ecSmrg    }
68605b261ecSmrg    if (rule->variant != NULL) {
68705b261ecSmrg	if (mdefs->variant[rule->variant_num] == NULL ||
68805b261ecSmrg	    *mdefs->variant[rule->variant_num] == '\0')
68905b261ecSmrg	    return 0;
69005b261ecSmrg        if (strcmp(rule->variant, "*") == 0) {
69105b261ecSmrg            pending = True;
69205b261ecSmrg        } else {
69305b261ecSmrg            if (rule->variant[0] == '$') {
69405b261ecSmrg               if (!CheckGroup(rules, rule->variant,
69505b261ecSmrg                               mdefs->variant[rule->variant_num]))
69605b261ecSmrg                  return 0;
69705b261ecSmrg            } else {
69805b261ecSmrg	       if (strcmp(rule->variant,
69905b261ecSmrg                          mdefs->variant[rule->variant_num]) != 0)
70005b261ecSmrg	           return 0;
70105b261ecSmrg	    }
70205b261ecSmrg	}
70305b261ecSmrg    }
70405b261ecSmrg    if (pending) {
70505b261ecSmrg        rule->flags|= XkbRF_PendingMatch;
70605b261ecSmrg	return rule->number;
70705b261ecSmrg    }
70805b261ecSmrg    /* exact match, apply it now */
70905b261ecSmrg    XkbRF_ApplyRule(rule,names);
71005b261ecSmrg    return rule->number;
71105b261ecSmrg}
71205b261ecSmrg
71305b261ecSmrgstatic void
71405b261ecSmrgXkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
71505b261ecSmrg{
71605b261ecSmrgregister int 	i;
71705b261ecSmrgXkbRF_RulePtr	rule;
71805b261ecSmrg
71905b261ecSmrg    for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
72005b261ecSmrg	rule->flags&= ~XkbRF_PendingMatch;
72105b261ecSmrg    }
72205b261ecSmrg}
72305b261ecSmrg
72405b261ecSmrgstatic void
72505b261ecSmrgXkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules,XkbComponentNamesPtr names)
72605b261ecSmrg{
72705b261ecSmrgint		i;
72805b261ecSmrgXkbRF_RulePtr	rule;
72905b261ecSmrg
73005b261ecSmrg    for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
73105b261ecSmrg	if ((rule->flags&XkbRF_PendingMatch)==0)
73205b261ecSmrg	    continue;
73305b261ecSmrg	XkbRF_ApplyRule(rule,names);
73405b261ecSmrg    }
73505b261ecSmrg}
73605b261ecSmrg
73705b261ecSmrgstatic void
73805b261ecSmrgXkbRF_CheckApplyRules(	XkbRF_RulesPtr 		rules,
73905b261ecSmrg			XkbRF_MultiDefsPtr	mdefs,
74005b261ecSmrg			XkbComponentNamesPtr	names,
74105b261ecSmrg			int			flags)
74205b261ecSmrg{
74305b261ecSmrgint		i;
74405b261ecSmrgXkbRF_RulePtr	rule;
74505b261ecSmrgint		skip;
74605b261ecSmrg
74705b261ecSmrg    for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
74805b261ecSmrg	if ((rule->flags & flags) != flags)
74905b261ecSmrg	    continue;
75005b261ecSmrg	skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
75105b261ecSmrg	if (skip && !(flags & XkbRF_Option)) {
75205b261ecSmrg	    for ( ;(i < rules->num_rules) && (rule->number == skip);
75305b261ecSmrg		  rule++, i++);
75405b261ecSmrg	    rule--; i--;
75505b261ecSmrg	}
75605b261ecSmrg    }
75705b261ecSmrg}
75805b261ecSmrg
75905b261ecSmrg/***====================================================================***/
76005b261ecSmrg
76105b261ecSmrgstatic char *
76205b261ecSmrgXkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
76305b261ecSmrg{
76405b261ecSmrgchar 	*str, *outstr, *orig, *var;
76505b261ecSmrgint	len, ndx;
76605b261ecSmrg
76705b261ecSmrg    orig= name;
76805b261ecSmrg    str= index(name,'%');
76905b261ecSmrg    if (str==NULL)
77005b261ecSmrg	return name;
77105b261ecSmrg    len= strlen(name);
77205b261ecSmrg    while (str!=NULL) {
77305b261ecSmrg	char pfx= str[1];
77405b261ecSmrg	int   extra_len= 0;
77505b261ecSmrg	if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
77605b261ecSmrg	    extra_len= 1;
77705b261ecSmrg	    str++;
77805b261ecSmrg	}
77905b261ecSmrg	else if (pfx=='(') {
78005b261ecSmrg	    extra_len= 2;
78105b261ecSmrg	    str++;
78205b261ecSmrg	}
78305b261ecSmrg	var = str + 1;
78405b261ecSmrg	str = get_index(var + 1, &ndx);
78505b261ecSmrg	if (ndx == -1) {
78605b261ecSmrg	    str = index(str,'%');
78705b261ecSmrg	    continue;
78805b261ecSmrg        }
78905b261ecSmrg	if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
79005b261ecSmrg	    len+= strlen(mdefs->layout[ndx])+extra_len;
79105b261ecSmrg	else if ((*var=='m')&&mdefs->model)
79205b261ecSmrg	    len+= strlen(mdefs->model)+extra_len;
79305b261ecSmrg	else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
79405b261ecSmrg	    len+= strlen(mdefs->variant[ndx])+extra_len;
79505b261ecSmrg	if ((pfx=='(')&&(*str==')')) {
79605b261ecSmrg	    str++;
79705b261ecSmrg	}
79805b261ecSmrg	str= index(&str[0],'%');
79905b261ecSmrg    }
80005b261ecSmrg    name= (char *)_XkbAlloc(len+1);
80105b261ecSmrg    str= orig;
80205b261ecSmrg    outstr= name;
80305b261ecSmrg    while (*str!='\0') {
80405b261ecSmrg	if (str[0]=='%') {
80505b261ecSmrg	    char pfx,sfx;
80605b261ecSmrg	    str++;
80705b261ecSmrg	    pfx= str[0];
80805b261ecSmrg	    sfx= '\0';
80905b261ecSmrg	    if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
81005b261ecSmrg		str++;
81105b261ecSmrg	    }
81205b261ecSmrg	    else if (pfx=='(') {
81305b261ecSmrg		sfx= ')';
81405b261ecSmrg		str++;
81505b261ecSmrg	    }
81605b261ecSmrg	    else pfx= '\0';
81705b261ecSmrg
81805b261ecSmrg	    var = str;
81905b261ecSmrg	    str = get_index(var + 1, &ndx);
82005b261ecSmrg	    if (ndx == -1) {
82105b261ecSmrg	        continue;
82205b261ecSmrg            }
82305b261ecSmrg	    if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
82405b261ecSmrg		if (pfx) *outstr++= pfx;
82505b261ecSmrg		strcpy(outstr,mdefs->layout[ndx]);
82605b261ecSmrg		outstr+= strlen(mdefs->layout[ndx]);
82705b261ecSmrg		if (sfx) *outstr++= sfx;
82805b261ecSmrg	    }
82905b261ecSmrg	    else if ((*var=='m')&&(mdefs->model)) {
83005b261ecSmrg		if (pfx) *outstr++= pfx;
83105b261ecSmrg		strcpy(outstr,mdefs->model);
83205b261ecSmrg		outstr+= strlen(mdefs->model);
83305b261ecSmrg		if (sfx) *outstr++= sfx;
83405b261ecSmrg	    }
83505b261ecSmrg	    else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
83605b261ecSmrg		if (pfx) *outstr++= pfx;
83705b261ecSmrg		strcpy(outstr,mdefs->variant[ndx]);
83805b261ecSmrg		outstr+= strlen(mdefs->variant[ndx]);
83905b261ecSmrg		if (sfx) *outstr++= sfx;
84005b261ecSmrg	    }
84105b261ecSmrg	    if ((pfx=='(')&&(*str==')'))
84205b261ecSmrg		str++;
84305b261ecSmrg	}
84405b261ecSmrg	else {
84505b261ecSmrg	    *outstr++= *str++;
84605b261ecSmrg	}
84705b261ecSmrg    }
84805b261ecSmrg    *outstr++= '\0';
84905b261ecSmrg    if (orig!=name)
85005b261ecSmrg	_XkbFree(orig);
85105b261ecSmrg    return name;
85205b261ecSmrg}
85305b261ecSmrg
85405b261ecSmrg/***====================================================================***/
85505b261ecSmrg
85605b261ecSmrgBool
85705b261ecSmrgXkbRF_GetComponents(	XkbRF_RulesPtr		rules,
85805b261ecSmrg			XkbRF_VarDefsPtr	defs,
85905b261ecSmrg			XkbComponentNamesPtr	names)
86005b261ecSmrg{
86105b261ecSmrg    XkbRF_MultiDefsRec mdefs;
86205b261ecSmrg
86305b261ecSmrg    MakeMultiDefs(&mdefs, defs);
86405b261ecSmrg
86505b261ecSmrg    bzero((char *)names,sizeof(XkbComponentNamesRec));
86605b261ecSmrg    XkbRF_ClearPartialMatches(rules);
86705b261ecSmrg    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
86805b261ecSmrg    XkbRF_ApplyPartialMatches(rules, names);
86905b261ecSmrg    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
87005b261ecSmrg    XkbRF_ApplyPartialMatches(rules, names);
87105b261ecSmrg    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
87205b261ecSmrg
87305b261ecSmrg    if (names->keycodes)
87405b261ecSmrg	names->keycodes= XkbRF_SubstituteVars(names->keycodes, &mdefs);
87505b261ecSmrg    if (names->symbols)
87605b261ecSmrg	names->symbols=	XkbRF_SubstituteVars(names->symbols, &mdefs);
87705b261ecSmrg    if (names->types)
87805b261ecSmrg	names->types= XkbRF_SubstituteVars(names->types, &mdefs);
87905b261ecSmrg    if (names->compat)
88005b261ecSmrg	names->compat= XkbRF_SubstituteVars(names->compat, &mdefs);
88105b261ecSmrg    if (names->geometry)
88205b261ecSmrg	names->geometry= XkbRF_SubstituteVars(names->geometry, &mdefs);
88305b261ecSmrg    if (names->keymap)
88405b261ecSmrg	names->keymap= XkbRF_SubstituteVars(names->keymap, &mdefs);
88505b261ecSmrg
88605b261ecSmrg    FreeMultiDefs(&mdefs);
88705b261ecSmrg    return (names->keycodes && names->symbols && names->types &&
88805b261ecSmrg		names->compat && names->geometry ) || names->keymap;
88905b261ecSmrg}
89005b261ecSmrg
89105b261ecSmrgXkbRF_RulePtr
89205b261ecSmrgXkbRF_AddRule(XkbRF_RulesPtr	rules)
89305b261ecSmrg{
89405b261ecSmrg    if (rules->sz_rules<1) {
89505b261ecSmrg	rules->sz_rules= 16;
89605b261ecSmrg	rules->num_rules= 0;
89705b261ecSmrg	rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
89805b261ecSmrg    }
89905b261ecSmrg    else if (rules->num_rules>=rules->sz_rules) {
90005b261ecSmrg	rules->sz_rules*= 2;
90105b261ecSmrg	rules->rules= _XkbTypedRealloc(rules->rules,rules->sz_rules,
90205b261ecSmrg							XkbRF_RuleRec);
90305b261ecSmrg    }
90405b261ecSmrg    if (!rules->rules) {
90505b261ecSmrg	rules->sz_rules= rules->num_rules= 0;
90605b261ecSmrg#ifdef DEBUG
90705b261ecSmrg	fprintf(stderr,"Allocation failure in XkbRF_AddRule\n");
90805b261ecSmrg#endif
90905b261ecSmrg	return NULL;
91005b261ecSmrg    }
91105b261ecSmrg    bzero((char *)&rules->rules[rules->num_rules],sizeof(XkbRF_RuleRec));
91205b261ecSmrg    return &rules->rules[rules->num_rules++];
91305b261ecSmrg}
91405b261ecSmrg
91505b261ecSmrgXkbRF_GroupPtr
91605b261ecSmrgXkbRF_AddGroup(XkbRF_RulesPtr	rules)
91705b261ecSmrg{
91805b261ecSmrg    if (rules->sz_groups<1) {
91905b261ecSmrg	rules->sz_groups= 16;
92005b261ecSmrg	rules->num_groups= 0;
92105b261ecSmrg	rules->groups= _XkbTypedCalloc(rules->sz_groups,XkbRF_GroupRec);
92205b261ecSmrg    }
92305b261ecSmrg    else if (rules->num_groups >= rules->sz_groups) {
92405b261ecSmrg	rules->sz_groups *= 2;
92505b261ecSmrg	rules->groups= _XkbTypedRealloc(rules->groups,rules->sz_groups,
92605b261ecSmrg							XkbRF_GroupRec);
92705b261ecSmrg    }
92805b261ecSmrg    if (!rules->groups) {
92905b261ecSmrg	rules->sz_groups= rules->num_groups= 0;
93005b261ecSmrg	return NULL;
93105b261ecSmrg    }
93205b261ecSmrg
93305b261ecSmrg    bzero((char *)&rules->groups[rules->num_groups],sizeof(XkbRF_GroupRec));
93405b261ecSmrg    return &rules->groups[rules->num_groups++];
93505b261ecSmrg}
93605b261ecSmrg
93705b261ecSmrgBool
93805b261ecSmrgXkbRF_LoadRules(FILE *file, XkbRF_RulesPtr rules)
93905b261ecSmrg{
94005b261ecSmrgInputLine	line;
94105b261ecSmrgRemapSpec	remap;
94205b261ecSmrgXkbRF_RuleRec	trule,*rule;
94305b261ecSmrgXkbRF_GroupRec  tgroup,*group;
94405b261ecSmrg
94505b261ecSmrg    if (!(rules && file))
94605b261ecSmrg	return False;
94705b261ecSmrg    bzero((char *)&remap,sizeof(RemapSpec));
94805b261ecSmrg    bzero((char *)&tgroup,sizeof(XkbRF_GroupRec));
94905b261ecSmrg    InitInputLine(&line);
95005b261ecSmrg    while (GetInputLine(file,&line,True)) {
95105b261ecSmrg	if (CheckLine(&line,&remap,&trule,&tgroup)) {
95205b261ecSmrg            if (tgroup.number) {
95305b261ecSmrg	        if ((group= XkbRF_AddGroup(rules))!=NULL) {
95405b261ecSmrg		    *group= tgroup;
95505b261ecSmrg		    bzero((char *)&tgroup,sizeof(XkbRF_GroupRec));
95605b261ecSmrg	        }
95705b261ecSmrg	    } else {
95805b261ecSmrg	        if ((rule= XkbRF_AddRule(rules))!=NULL) {
95905b261ecSmrg		    *rule= trule;
96005b261ecSmrg		    bzero((char *)&trule,sizeof(XkbRF_RuleRec));
96105b261ecSmrg	        }
96205b261ecSmrg	    }
96305b261ecSmrg	}
96405b261ecSmrg	line.num_line= 0;
96505b261ecSmrg    }
96605b261ecSmrg    FreeInputLine(&line);
96705b261ecSmrg    return True;
96805b261ecSmrg}
96905b261ecSmrg
97005b261ecSmrgBool
97105b261ecSmrgXkbRF_LoadRulesByName(char *base,char *locale,XkbRF_RulesPtr rules)
97205b261ecSmrg{
97305b261ecSmrgFILE *		file;
97405b261ecSmrgchar		buf[PATH_MAX];
97505b261ecSmrgBool		ok;
97605b261ecSmrg
97705b261ecSmrg    if ((!base)||(!rules))
97805b261ecSmrg	return False;
97905b261ecSmrg    if (locale) {
98005b261ecSmrg	if (strlen(base)+strlen(locale)+2 > PATH_MAX)
98105b261ecSmrg	    return False;
98205b261ecSmrg	sprintf(buf,"%s-%s", base, locale);
98305b261ecSmrg    }
98405b261ecSmrg    else {
98505b261ecSmrg	if (strlen(base)+1 > PATH_MAX)
98605b261ecSmrg	    return False;
98705b261ecSmrg	strcpy(buf,base);
98805b261ecSmrg    }
98905b261ecSmrg
99005b261ecSmrg    file= fopen(buf, "r");
99105b261ecSmrg    if ((!file)&&(locale)) { /* fallback if locale was specified */
99205b261ecSmrg	strcpy(buf,base);
99305b261ecSmrg	file= fopen(buf, "r");
99405b261ecSmrg    }
99505b261ecSmrg    if (!file)
99605b261ecSmrg	return False;
99705b261ecSmrg    ok= XkbRF_LoadRules(file,rules);
99805b261ecSmrg    fclose(file);
99905b261ecSmrg    return ok;
100005b261ecSmrg}
100105b261ecSmrg
100205b261ecSmrg/***====================================================================***/
100305b261ecSmrg
100405b261ecSmrg#define HEAD_NONE	0
100505b261ecSmrg#define HEAD_MODEL	1
100605b261ecSmrg#define HEAD_LAYOUT	2
100705b261ecSmrg#define HEAD_VARIANT	3
100805b261ecSmrg#define HEAD_OPTION	4
100905b261ecSmrg#define	HEAD_EXTRA	5
101005b261ecSmrg
101105b261ecSmrgXkbRF_VarDescPtr
101205b261ecSmrgXkbRF_AddVarDesc(XkbRF_DescribeVarsPtr	vars)
101305b261ecSmrg{
101405b261ecSmrg    if (vars->sz_desc<1) {
101505b261ecSmrg	vars->sz_desc= 16;
101605b261ecSmrg	vars->num_desc= 0;
101705b261ecSmrg	vars->desc= _XkbTypedCalloc(vars->sz_desc,XkbRF_VarDescRec);
101805b261ecSmrg    }
101905b261ecSmrg    else if (vars->num_desc>=vars->sz_desc) {
102005b261ecSmrg	vars->sz_desc*= 2;
102105b261ecSmrg	vars->desc= _XkbTypedRealloc(vars->desc,vars->sz_desc,XkbRF_VarDescRec);
102205b261ecSmrg    }
102305b261ecSmrg    if (!vars->desc) {
102405b261ecSmrg	vars->sz_desc= vars->num_desc= 0;
102505b261ecSmrg	PR_DEBUG("Allocation failure in XkbRF_AddVarDesc\n");
102605b261ecSmrg	return NULL;
102705b261ecSmrg    }
102805b261ecSmrg    vars->desc[vars->num_desc].name= NULL;
102905b261ecSmrg    vars->desc[vars->num_desc].desc= NULL;
103005b261ecSmrg    return &vars->desc[vars->num_desc++];
103105b261ecSmrg}
103205b261ecSmrg
103305b261ecSmrgXkbRF_VarDescPtr
103405b261ecSmrgXkbRF_AddVarDescCopy(XkbRF_DescribeVarsPtr vars,XkbRF_VarDescPtr from)
103505b261ecSmrg{
103605b261ecSmrgXkbRF_VarDescPtr	nd;
103705b261ecSmrg
103805b261ecSmrg    if ((nd=XkbRF_AddVarDesc(vars))!=NULL) {
103905b261ecSmrg	nd->name= _XkbDupString(from->name);
104005b261ecSmrg	nd->desc= _XkbDupString(from->desc);
104105b261ecSmrg    }
104205b261ecSmrg    return nd;
104305b261ecSmrg}
104405b261ecSmrg
104505b261ecSmrgXkbRF_DescribeVarsPtr
104605b261ecSmrgXkbRF_AddVarToDescribe(XkbRF_RulesPtr rules,char *name)
104705b261ecSmrg{
104805b261ecSmrg    if (rules->sz_extra<1) {
104905b261ecSmrg	rules->num_extra= 0;
105005b261ecSmrg	rules->sz_extra= 1;
105105b261ecSmrg	rules->extra_names= _XkbTypedCalloc(rules->sz_extra,char *);
105205b261ecSmrg	rules->extra= _XkbTypedCalloc(rules->sz_extra, XkbRF_DescribeVarsRec);
105305b261ecSmrg    }
105405b261ecSmrg    else if (rules->num_extra>=rules->sz_extra) {
105505b261ecSmrg	rules->sz_extra*= 2;
105605b261ecSmrg	rules->extra_names= _XkbTypedRealloc(rules->extra_names,rules->sz_extra,
105705b261ecSmrg								char *);
105805b261ecSmrg	rules->extra=_XkbTypedRealloc(rules->extra, rules->sz_extra,
105905b261ecSmrg							XkbRF_DescribeVarsRec);
106005b261ecSmrg    }
106105b261ecSmrg    if ((!rules->extra_names)||(!rules->extra)) {
106205b261ecSmrg	PR_DEBUG("allocation error in extra parts\n");
106305b261ecSmrg	rules->sz_extra= rules->num_extra= 0;
106405b261ecSmrg	rules->extra_names= NULL;
106505b261ecSmrg	rules->extra= NULL;
106605b261ecSmrg	return NULL;
106705b261ecSmrg    }
106805b261ecSmrg    rules->extra_names[rules->num_extra]= _XkbDupString(name);
106905b261ecSmrg    bzero(&rules->extra[rules->num_extra],sizeof(XkbRF_DescribeVarsRec));
107005b261ecSmrg    return &rules->extra[rules->num_extra++];
107105b261ecSmrg}
107205b261ecSmrg
107305b261ecSmrgBool
107405b261ecSmrgXkbRF_LoadDescriptions(FILE *file,XkbRF_RulesPtr rules)
107505b261ecSmrg{
107605b261ecSmrgInputLine		line;
107705b261ecSmrgXkbRF_VarDescRec	tmp;
107805b261ecSmrgchar			*tok;
107905b261ecSmrgint			len,headingtype,extra_ndx = 0;
108005b261ecSmrg
108105b261ecSmrg    bzero((char *)&tmp, sizeof(XkbRF_VarDescRec));
108205b261ecSmrg    headingtype = HEAD_NONE;
108305b261ecSmrg    InitInputLine(&line);
108405b261ecSmrg    for ( ; GetInputLine(file,&line,False); line.num_line= 0) {
108505b261ecSmrg	if (line.line[0]=='!') {
108605b261ecSmrg	    tok = strtok(&(line.line[1]), " \t");
108705b261ecSmrg	    if (strcasecmp(tok,"model") == 0)
108805b261ecSmrg		headingtype = HEAD_MODEL;
108905b261ecSmrg	    else if (strcasecmp(tok,"layout") == 0)
109005b261ecSmrg		headingtype = HEAD_LAYOUT;
109105b261ecSmrg	    else if (strcasecmp(tok,"variant") == 0)
109205b261ecSmrg		headingtype = HEAD_VARIANT;
109305b261ecSmrg	    else if (strcasecmp(tok,"option") == 0)
109405b261ecSmrg		headingtype = HEAD_OPTION;
109505b261ecSmrg	    else {
109605b261ecSmrg		int i;
109705b261ecSmrg		headingtype = HEAD_EXTRA;
109805b261ecSmrg		extra_ndx= -1;
109905b261ecSmrg		for (i=0;(i<rules->num_extra)&&(extra_ndx<0);i++) {
110005b261ecSmrg		    if (!strcasecmp(tok,rules->extra_names[i]))
110105b261ecSmrg			extra_ndx= i;
110205b261ecSmrg		}
110305b261ecSmrg		if (extra_ndx<0) {
110405b261ecSmrg		    XkbRF_DescribeVarsPtr	var;
110505b261ecSmrg		    PR_DEBUG1("Extra heading \"%s\" encountered\n",tok);
110605b261ecSmrg		    var= XkbRF_AddVarToDescribe(rules,tok);
110705b261ecSmrg		    if (var)
110805b261ecSmrg			 extra_ndx= var-rules->extra;
110905b261ecSmrg		    else headingtype= HEAD_NONE;
111005b261ecSmrg		}
111105b261ecSmrg	    }
111205b261ecSmrg	    continue;
111305b261ecSmrg	}
111405b261ecSmrg
111505b261ecSmrg	if (headingtype == HEAD_NONE) {
111605b261ecSmrg	    PR_DEBUG("Must have a heading before first line of data\n");
111705b261ecSmrg	    PR_DEBUG("Illegal line of data ignored\n");
111805b261ecSmrg	    continue;
111905b261ecSmrg	}
112005b261ecSmrg
112105b261ecSmrg	len = strlen(line.line);
112205b261ecSmrg	if ((tmp.name= strtok(line.line, " \t")) == NULL) {
112305b261ecSmrg	    PR_DEBUG("Huh? No token on line\n");
112405b261ecSmrg	    PR_DEBUG("Illegal line of data ignored\n");
112505b261ecSmrg	    continue;
112605b261ecSmrg	}
112705b261ecSmrg	if (strlen(tmp.name) == len) {
112805b261ecSmrg	    PR_DEBUG("No description found\n");
112905b261ecSmrg	    PR_DEBUG("Illegal line of data ignored\n");
113005b261ecSmrg	    continue;
113105b261ecSmrg	}
113205b261ecSmrg
113305b261ecSmrg	tok = line.line + strlen(tmp.name) + 1;
113405b261ecSmrg	while ((*tok!='\n')&&isspace(*tok))
113505b261ecSmrg		tok++;
113605b261ecSmrg	if (*tok == '\0') {
113705b261ecSmrg	    PR_DEBUG("No description found\n");
113805b261ecSmrg	    PR_DEBUG("Illegal line of data ignored\n");
113905b261ecSmrg	    continue;
114005b261ecSmrg	}
114105b261ecSmrg	tmp.desc= tok;
114205b261ecSmrg	switch (headingtype) {
114305b261ecSmrg	    case HEAD_MODEL:
114405b261ecSmrg		XkbRF_AddVarDescCopy(&rules->models,&tmp);
114505b261ecSmrg		break;
114605b261ecSmrg	    case HEAD_LAYOUT:
114705b261ecSmrg		XkbRF_AddVarDescCopy(&rules->layouts,&tmp);
114805b261ecSmrg		break;
114905b261ecSmrg	    case HEAD_VARIANT:
115005b261ecSmrg		XkbRF_AddVarDescCopy(&rules->variants,&tmp);
115105b261ecSmrg		break;
115205b261ecSmrg	    case HEAD_OPTION:
115305b261ecSmrg		XkbRF_AddVarDescCopy(&rules->options,&tmp);
115405b261ecSmrg		break;
115505b261ecSmrg	    case HEAD_EXTRA:
115605b261ecSmrg		XkbRF_AddVarDescCopy(&rules->extra[extra_ndx],&tmp);
115705b261ecSmrg		break;
115805b261ecSmrg	}
115905b261ecSmrg    }
116005b261ecSmrg    FreeInputLine(&line);
116105b261ecSmrg    if ((rules->models.num_desc==0) && (rules->layouts.num_desc==0) &&
116205b261ecSmrg	(rules->variants.num_desc==0) && (rules->options.num_desc==0) &&
116305b261ecSmrg	(rules->num_extra==0)) {
116405b261ecSmrg	return False;
116505b261ecSmrg    }
116605b261ecSmrg    return True;
116705b261ecSmrg}
116805b261ecSmrg
116905b261ecSmrgBool
117005b261ecSmrgXkbRF_LoadDescriptionsByName(char *base,char *locale,XkbRF_RulesPtr rules)
117105b261ecSmrg{
117205b261ecSmrgFILE *		file;
117305b261ecSmrgchar		buf[PATH_MAX];
117405b261ecSmrgBool		ok;
117505b261ecSmrg
117605b261ecSmrg    if ((!base)||(!rules))
117705b261ecSmrg	return False;
117805b261ecSmrg    if (locale) {
117905b261ecSmrg	if (strlen(base)+strlen(locale)+6 > PATH_MAX)
118005b261ecSmrg	    return False;
118105b261ecSmrg	sprintf(buf,"%s-%s.lst", base, locale);
118205b261ecSmrg    }
118305b261ecSmrg    else {
118405b261ecSmrg	if (strlen(base)+5 > PATH_MAX)
118505b261ecSmrg	    return False;
118605b261ecSmrg	sprintf(buf,"%s.lst", base);
118705b261ecSmrg    }
118805b261ecSmrg
118905b261ecSmrg    file= fopen(buf, "r");
119005b261ecSmrg    if ((!file)&&(locale)) { /* fallback if locale was specified */
119105b261ecSmrg	sprintf(buf,"%s.lst", base);
119205b261ecSmrg
119305b261ecSmrg	file= fopen(buf, "r");
119405b261ecSmrg    }
119505b261ecSmrg    if (!file)
119605b261ecSmrg	return False;
119705b261ecSmrg    ok= XkbRF_LoadDescriptions(file,rules);
119805b261ecSmrg    fclose(file);
119905b261ecSmrg    return ok;
120005b261ecSmrg}
120105b261ecSmrg
120205b261ecSmrg/***====================================================================***/
120305b261ecSmrg
120405b261ecSmrgXkbRF_RulesPtr
120505b261ecSmrgXkbRF_Load(char *base,char *locale,Bool wantDesc,Bool wantRules)
120605b261ecSmrg{
120705b261ecSmrgXkbRF_RulesPtr	rules;
120805b261ecSmrg
120905b261ecSmrg    if ((!base)||((!wantDesc)&&(!wantRules)))
121005b261ecSmrg	return NULL;
121105b261ecSmrg    if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
121205b261ecSmrg	return NULL;
121305b261ecSmrg    if (wantDesc&&(!XkbRF_LoadDescriptionsByName(base,locale,rules))) {
121405b261ecSmrg	XkbRF_Free(rules,True);
121505b261ecSmrg	return NULL;
121605b261ecSmrg    }
121705b261ecSmrg    if (wantRules&&(!XkbRF_LoadRulesByName(base,locale,rules))) {
121805b261ecSmrg	XkbRF_Free(rules,True);
121905b261ecSmrg	return NULL;
122005b261ecSmrg    }
122105b261ecSmrg    return rules;
122205b261ecSmrg}
122305b261ecSmrg
122405b261ecSmrgXkbRF_RulesPtr
122505b261ecSmrgXkbRF_Create(int szRules,int szExtra)
122605b261ecSmrg{
122705b261ecSmrgXkbRF_RulesPtr rules;
122805b261ecSmrg
122905b261ecSmrg    if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
123005b261ecSmrg	return NULL;
123105b261ecSmrg    if (szRules>0) {
123205b261ecSmrg	rules->sz_rules= szRules;
123305b261ecSmrg	rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
123405b261ecSmrg	if (!rules->rules) {
123505b261ecSmrg	    _XkbFree(rules);
123605b261ecSmrg	    return NULL;
123705b261ecSmrg	}
123805b261ecSmrg    }
123905b261ecSmrg    if (szExtra>0) {
124005b261ecSmrg	rules->sz_extra= szExtra;
124105b261ecSmrg	rules->extra= _XkbTypedCalloc(rules->sz_extra,XkbRF_DescribeVarsRec);
124205b261ecSmrg	if (!rules->extra) {
124305b261ecSmrg	    if (rules->rules)
124405b261ecSmrg		_XkbFree(rules->rules);
124505b261ecSmrg	    _XkbFree(rules);
124605b261ecSmrg	    return NULL;
124705b261ecSmrg	}
124805b261ecSmrg    }
124905b261ecSmrg    return rules;
125005b261ecSmrg}
125105b261ecSmrg
125205b261ecSmrg/***====================================================================***/
125305b261ecSmrg
125405b261ecSmrgstatic void
125505b261ecSmrgXkbRF_ClearVarDescriptions(XkbRF_DescribeVarsPtr var)
125605b261ecSmrg{
125705b261ecSmrgregister int i;
125805b261ecSmrg
125905b261ecSmrg    for (i=0;i<var->num_desc;i++) {
126005b261ecSmrg	if (var->desc[i].name)
126105b261ecSmrg	    _XkbFree(var->desc[i].name);
126205b261ecSmrg	if (var->desc[i].desc)
126305b261ecSmrg	    _XkbFree(var->desc[i].desc);
126405b261ecSmrg	var->desc[i].name= var->desc[i].desc= NULL;
126505b261ecSmrg    }
126605b261ecSmrg    if (var->desc)
126705b261ecSmrg	_XkbFree(var->desc);
126805b261ecSmrg    var->desc= NULL;
126905b261ecSmrg    return;
127005b261ecSmrg}
127105b261ecSmrg
127205b261ecSmrgvoid
127305b261ecSmrgXkbRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
127405b261ecSmrg{
127505b261ecSmrgint		i;
127605b261ecSmrgXkbRF_RulePtr	rule;
127705b261ecSmrgXkbRF_GroupPtr	group;
127805b261ecSmrg
127905b261ecSmrg    if (!rules)
128005b261ecSmrg	return;
128105b261ecSmrg    XkbRF_ClearVarDescriptions(&rules->models);
128205b261ecSmrg    XkbRF_ClearVarDescriptions(&rules->layouts);
128305b261ecSmrg    XkbRF_ClearVarDescriptions(&rules->variants);
128405b261ecSmrg    XkbRF_ClearVarDescriptions(&rules->options);
128505b261ecSmrg    if (rules->extra) {
128605b261ecSmrg	for (i = 0; i < rules->num_extra; i++) {
128705b261ecSmrg	    XkbRF_ClearVarDescriptions(&rules->extra[i]);
128805b261ecSmrg	}
128905b261ecSmrg	_XkbFree(rules->extra);
129005b261ecSmrg	rules->num_extra= rules->sz_extra= 0;
129105b261ecSmrg	rules->extra= NULL;
129205b261ecSmrg    }
129305b261ecSmrg    if (rules->rules) {
129405b261ecSmrg	for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
129505b261ecSmrg	    if (rule->model)	_XkbFree(rule->model);
129605b261ecSmrg	    if (rule->layout)	_XkbFree(rule->layout);
129705b261ecSmrg	    if (rule->variant)	_XkbFree(rule->variant);
129805b261ecSmrg	    if (rule->option)	_XkbFree(rule->option);
129905b261ecSmrg	    if (rule->keycodes)	_XkbFree(rule->keycodes);
130005b261ecSmrg	    if (rule->symbols)	_XkbFree(rule->symbols);
130105b261ecSmrg	    if (rule->types)	_XkbFree(rule->types);
130205b261ecSmrg	    if (rule->compat)	_XkbFree(rule->compat);
130305b261ecSmrg	    if (rule->geometry)	_XkbFree(rule->geometry);
130405b261ecSmrg	    if (rule->keymap)	_XkbFree(rule->keymap);
130505b261ecSmrg	    bzero((char *)rule,sizeof(XkbRF_RuleRec));
130605b261ecSmrg	}
130705b261ecSmrg	_XkbFree(rules->rules);
130805b261ecSmrg	rules->num_rules= rules->sz_rules= 0;
130905b261ecSmrg	rules->rules= NULL;
131005b261ecSmrg    }
131105b261ecSmrg
131205b261ecSmrg    if (rules->groups) {
131305b261ecSmrg	for (i=0, group=rules->groups;i<rules->num_groups;i++,group++) {
131405b261ecSmrg	    if (group->name)	_XkbFree(group->name);
131505b261ecSmrg	    if (group->words)	_XkbFree(group->words);
131605b261ecSmrg	}
131705b261ecSmrg	_XkbFree(rules->groups);
131805b261ecSmrg	rules->num_groups= 0;
131905b261ecSmrg	rules->groups= NULL;
132005b261ecSmrg    }
132105b261ecSmrg    if (freeRules)
132205b261ecSmrg	_XkbFree(rules);
132305b261ecSmrg    return;
132405b261ecSmrg}
1325