1706f2543Smrg/************************************************************
2706f2543Smrg Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
3706f2543Smrg
4706f2543Smrg Permission to use, copy, modify, and distribute this
5706f2543Smrg software and its documentation for any purpose and without
6706f2543Smrg fee is hereby granted, provided that the above copyright
7706f2543Smrg notice appear in all copies and that both that copyright
8706f2543Smrg notice and this permission notice appear in supporting
9706f2543Smrg documentation, and that the name of Silicon Graphics not be
10706f2543Smrg used in advertising or publicity pertaining to distribution
11706f2543Smrg of the software without specific prior written permission.
12706f2543Smrg Silicon Graphics makes no representation about the suitability
13706f2543Smrg of this software for any purpose. It is provided "as is"
14706f2543Smrg without any express or implied warranty.
15706f2543Smrg
16706f2543Smrg SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17706f2543Smrg SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18706f2543Smrg AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19706f2543Smrg GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20706f2543Smrg DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21706f2543Smrg DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22706f2543Smrg OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23706f2543Smrg THE USE OR PERFORMANCE OF THIS SOFTWARE.
24706f2543Smrg
25706f2543Smrg ********************************************************/
26706f2543Smrg
27706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
28706f2543Smrg#include <dix-config.h>
29706f2543Smrg#endif
30706f2543Smrg
31706f2543Smrg#include <stdio.h>
32706f2543Smrg#include <ctype.h>
33706f2543Smrg#include <stdlib.h>
34706f2543Smrg
35706f2543Smrg#define X_INCLUDE_STRING_H
36706f2543Smrg#define XOS_USE_NO_LOCKING
37706f2543Smrg#include <X11/Xos_r.h>
38706f2543Smrg
39706f2543Smrg#include <X11/Xproto.h>
40706f2543Smrg#include <X11/X.h>
41706f2543Smrg#include <X11/Xos.h>
42706f2543Smrg#include <X11/Xfuncs.h>
43706f2543Smrg#include <X11/Xatom.h>
44706f2543Smrg#include <X11/keysym.h>
45706f2543Smrg#include "misc.h"
46706f2543Smrg#include "inputstr.h"
47706f2543Smrg#include "dix.h"
48706f2543Smrg#include "os.h"
49706f2543Smrg#include "xkbstr.h"
50706f2543Smrg#define XKBSRV_NEED_FILE_FUNCS
51706f2543Smrg#include <xkbsrv.h>
52706f2543Smrg
53706f2543Smrg/***====================================================================***/
54706f2543Smrg
55706f2543Smrg
56706f2543Smrg
57706f2543Smrg#define DFLT_LINE_SIZE	128
58706f2543Smrg
59706f2543Smrgtypedef struct {
60706f2543Smrg	int	line_num;
61706f2543Smrg	int	sz_line;
62706f2543Smrg	int	num_line;
63706f2543Smrg	char	buf[DFLT_LINE_SIZE];
64706f2543Smrg	char *	line;
65706f2543Smrg} InputLine;
66706f2543Smrg
67706f2543Smrgstatic void
68706f2543SmrgInitInputLine(InputLine *line)
69706f2543Smrg{
70706f2543Smrg    line->line_num= 1;
71706f2543Smrg    line->num_line= 0;
72706f2543Smrg    line->sz_line= DFLT_LINE_SIZE;
73706f2543Smrg    line->line=	line->buf;
74706f2543Smrg    return;
75706f2543Smrg}
76706f2543Smrg
77706f2543Smrgstatic void
78706f2543SmrgFreeInputLine(InputLine *line)
79706f2543Smrg{
80706f2543Smrg    if (line->line!=line->buf)
81706f2543Smrg	free(line->line);
82706f2543Smrg    line->line_num= 1;
83706f2543Smrg    line->num_line= 0;
84706f2543Smrg    line->sz_line= DFLT_LINE_SIZE;
85706f2543Smrg    line->line= line->buf;
86706f2543Smrg    return;
87706f2543Smrg}
88706f2543Smrg
89706f2543Smrgstatic int
90706f2543SmrgInputLineAddChar(InputLine *line,int ch)
91706f2543Smrg{
92706f2543Smrg    if (line->num_line>=line->sz_line) {
93706f2543Smrg	if (line->line==line->buf) {
94706f2543Smrg	    line->line= malloc(line->sz_line*2);
95706f2543Smrg	    memcpy(line->line,line->buf,line->sz_line);
96706f2543Smrg	}
97706f2543Smrg	else {
98706f2543Smrg	    line->line= realloc((char *)line->line,line->sz_line*2);
99706f2543Smrg	}
100706f2543Smrg	line->sz_line*= 2;
101706f2543Smrg    }
102706f2543Smrg    line->line[line->num_line++]= ch;
103706f2543Smrg    return ch;
104706f2543Smrg}
105706f2543Smrg
106706f2543Smrg#define	ADD_CHAR(l,c)	((l)->num_line<(l)->sz_line?\
107706f2543Smrg				(int)((l)->line[(l)->num_line++]= (c)):\
108706f2543Smrg				InputLineAddChar(l,c))
109706f2543Smrg
110706f2543Smrgstatic Bool
111706f2543SmrgGetInputLine(FILE *file,InputLine *line,Bool checkbang)
112706f2543Smrg{
113706f2543Smrgint	ch;
114706f2543SmrgBool	endOfFile,spacePending,slashPending,inComment;
115706f2543Smrg
116706f2543Smrg     endOfFile= FALSE;
117706f2543Smrg     while ((!endOfFile)&&(line->num_line==0)) {
118706f2543Smrg	spacePending= slashPending= inComment= FALSE;
119706f2543Smrg	while (((ch=getc(file))!='\n')&&(ch!=EOF)) {
120706f2543Smrg	    if (ch=='\\') {
121706f2543Smrg		if ((ch=getc(file))==EOF)
122706f2543Smrg		    break;
123706f2543Smrg		if (ch=='\n') {
124706f2543Smrg		    inComment= FALSE;
125706f2543Smrg		    ch= ' ';
126706f2543Smrg		    line->line_num++;
127706f2543Smrg		}
128706f2543Smrg	    }
129706f2543Smrg	    if (inComment)
130706f2543Smrg		continue;
131706f2543Smrg	    if (ch=='/') {
132706f2543Smrg		if (slashPending) {
133706f2543Smrg		    inComment= TRUE;
134706f2543Smrg		    slashPending= FALSE;
135706f2543Smrg		}
136706f2543Smrg		else {
137706f2543Smrg		    slashPending= TRUE;
138706f2543Smrg		}
139706f2543Smrg		continue;
140706f2543Smrg	    }
141706f2543Smrg	    else if (slashPending) {
142706f2543Smrg		if (spacePending) {
143706f2543Smrg		    ADD_CHAR(line,' ');
144706f2543Smrg		    spacePending= FALSE;
145706f2543Smrg		}
146706f2543Smrg		ADD_CHAR(line,'/');
147706f2543Smrg		slashPending= FALSE;
148706f2543Smrg	    }
149706f2543Smrg	    if (isspace(ch)) {
150706f2543Smrg		while (isspace(ch)&&(ch!='\n')&&(ch!=EOF)) {
151706f2543Smrg		    ch= getc(file);
152706f2543Smrg		}
153706f2543Smrg		if (ch==EOF)
154706f2543Smrg		    break;
155706f2543Smrg		if ((ch!='\n')&&(line->num_line>0))
156706f2543Smrg		    spacePending= TRUE;
157706f2543Smrg		ungetc(ch,file);
158706f2543Smrg	    }
159706f2543Smrg	    else {
160706f2543Smrg		if (spacePending) {
161706f2543Smrg		    ADD_CHAR(line,' ');
162706f2543Smrg		    spacePending= FALSE;
163706f2543Smrg		}
164706f2543Smrg		if (checkbang && ch=='!') {
165706f2543Smrg		    if (line->num_line!=0) {
166706f2543Smrg			DebugF("The '!' legal only at start of line\n");
167706f2543Smrg			DebugF("Line containing '!' ignored\n");
168706f2543Smrg			line->num_line= 0;
169706f2543Smrg			inComment= 0;
170706f2543Smrg			break;
171706f2543Smrg		    }
172706f2543Smrg
173706f2543Smrg		}
174706f2543Smrg		ADD_CHAR(line,ch);
175706f2543Smrg	    }
176706f2543Smrg	}
177706f2543Smrg	if (ch==EOF)
178706f2543Smrg	     endOfFile= TRUE;
179706f2543Smrg/*	else line->num_line++;*/
180706f2543Smrg     }
181706f2543Smrg     if ((line->num_line==0)&&(endOfFile))
182706f2543Smrg	return FALSE;
183706f2543Smrg      ADD_CHAR(line,'\0');
184706f2543Smrg      return TRUE;
185706f2543Smrg}
186706f2543Smrg
187706f2543Smrg/***====================================================================***/
188706f2543Smrg
189706f2543Smrg#define	MODEL		0
190706f2543Smrg#define	LAYOUT		1
191706f2543Smrg#define	VARIANT		2
192706f2543Smrg#define	OPTION		3
193706f2543Smrg#define	KEYCODES	4
194706f2543Smrg#define SYMBOLS		5
195706f2543Smrg#define	TYPES		6
196706f2543Smrg#define	COMPAT		7
197706f2543Smrg#define	GEOMETRY	8
198706f2543Smrg#define	MAX_WORDS	9
199706f2543Smrg
200706f2543Smrg#define	PART_MASK	0x000F
201706f2543Smrg#define	COMPONENT_MASK	0x03F0
202706f2543Smrg
203706f2543Smrgstatic	char *	cname[MAX_WORDS] = {
204706f2543Smrg	"model", "layout", "variant", "option",
205706f2543Smrg	"keycodes", "symbols", "types", "compat", "geometry"
206706f2543Smrg};
207706f2543Smrg
208706f2543Smrgtypedef	struct _RemapSpec {
209706f2543Smrg	int			number;
210706f2543Smrg	int			num_remap;
211706f2543Smrg	struct	{
212706f2543Smrg		int	word;
213706f2543Smrg		int	index;
214706f2543Smrg                }		remap[MAX_WORDS];
215706f2543Smrg} RemapSpec;
216706f2543Smrg
217706f2543Smrgtypedef struct _FileSpec {
218706f2543Smrg	char *			name[MAX_WORDS];
219706f2543Smrg	struct _FileSpec *	pending;
220706f2543Smrg} FileSpec;
221706f2543Smrg
222706f2543Smrgtypedef struct {
223706f2543Smrg	char *			model;
224706f2543Smrg	char *			layout[XkbNumKbdGroups+1];
225706f2543Smrg	char *			variant[XkbNumKbdGroups+1];
226706f2543Smrg	char *			options;
227706f2543Smrg} XkbRF_MultiDefsRec, *XkbRF_MultiDefsPtr;
228706f2543Smrg
229706f2543Smrg#define NDX_BUFF_SIZE	4
230706f2543Smrg
231706f2543Smrg/***====================================================================***/
232706f2543Smrg
233706f2543Smrgstatic char*
234706f2543Smrgget_index(char *str, int *ndx)
235706f2543Smrg{
236706f2543Smrg   char ndx_buf[NDX_BUFF_SIZE];
237706f2543Smrg   char *end;
238706f2543Smrg
239706f2543Smrg   if (*str != '[') {
240706f2543Smrg       *ndx = 0;
241706f2543Smrg       return str;
242706f2543Smrg   }
243706f2543Smrg   str++;
244706f2543Smrg   end = strchr(str, ']');
245706f2543Smrg   if (end == NULL) {
246706f2543Smrg       *ndx = -1;
247706f2543Smrg       return str - 1;
248706f2543Smrg   }
249706f2543Smrg   if ( (end - str) >= NDX_BUFF_SIZE) {
250706f2543Smrg       *ndx = -1;
251706f2543Smrg       return end + 1;
252706f2543Smrg   }
253706f2543Smrg   strncpy(ndx_buf, str, end - str);
254706f2543Smrg   ndx_buf[end - str] = '\0';
255706f2543Smrg   *ndx = atoi(ndx_buf);
256706f2543Smrg   return end + 1;
257706f2543Smrg}
258706f2543Smrg
259706f2543Smrgstatic void
260706f2543SmrgSetUpRemap(InputLine *line,RemapSpec *remap)
261706f2543Smrg{
262706f2543Smrgchar *		tok,*str;
263706f2543Smrgunsigned	present, l_ndx_present, v_ndx_present;
264706f2543Smrgregister int	i;
265706f2543Smrgint		len, ndx;
266706f2543Smrg_Xstrtokparams	strtok_buf;
267706f2543SmrgBool		found;
268706f2543Smrg
269706f2543Smrg
270706f2543Smrg   l_ndx_present = v_ndx_present = present= 0;
271706f2543Smrg   str= &line->line[1];
272706f2543Smrg   len = remap->number;
273706f2543Smrg   memset((char *)remap, 0, sizeof(RemapSpec));
274706f2543Smrg   remap->number = len;
275706f2543Smrg   while ((tok=_XStrtok(str," ",strtok_buf))!=NULL) {
276706f2543Smrg	found= FALSE;
277706f2543Smrg	str= NULL;
278706f2543Smrg	if (strcmp(tok,"=")==0)
279706f2543Smrg	    continue;
280706f2543Smrg	for (i=0;i<MAX_WORDS;i++) {
281706f2543Smrg            len = strlen(cname[i]);
282706f2543Smrg	    if (strncmp(cname[i],tok,len)==0) {
283706f2543Smrg		if(strlen(tok) > len) {
284706f2543Smrg		    char *end = get_index(tok+len, &ndx);
285706f2543Smrg		    if ((i != LAYOUT && i != VARIANT) ||
286706f2543Smrg			*end != '\0' || ndx == -1)
287706f2543Smrg		        break;
288706f2543Smrg		     if (ndx < 1 || ndx > XkbNumKbdGroups) {
289706f2543Smrg		        DebugF("Illegal %s index: %d\n", cname[i], ndx);
290706f2543Smrg		        DebugF("Index must be in range 1..%d\n",
291706f2543Smrg				   XkbNumKbdGroups);
292706f2543Smrg			break;
293706f2543Smrg		     }
294706f2543Smrg                } else {
295706f2543Smrg		    ndx = 0;
296706f2543Smrg                }
297706f2543Smrg		found= TRUE;
298706f2543Smrg		if (present&(1<<i)) {
299706f2543Smrg		    if ((i == LAYOUT && l_ndx_present&(1<<ndx)) ||
300706f2543Smrg			(i == VARIANT && v_ndx_present&(1<<ndx)) ) {
301706f2543Smrg		        DebugF("Component \"%s\" listed twice\n",tok);
302706f2543Smrg		        DebugF("Second definition ignored\n");
303706f2543Smrg		        break;
304706f2543Smrg		    }
305706f2543Smrg		}
306706f2543Smrg		present |= (1<<i);
307706f2543Smrg                if (i == LAYOUT)
308706f2543Smrg                    l_ndx_present |= 1 << ndx;
309706f2543Smrg                if (i == VARIANT)
310706f2543Smrg                    v_ndx_present |= 1 << ndx;
311706f2543Smrg		remap->remap[remap->num_remap].word= i;
312706f2543Smrg		remap->remap[remap->num_remap++].index= ndx;
313706f2543Smrg		break;
314706f2543Smrg	    }
315706f2543Smrg	}
316706f2543Smrg	if (!found) {
317706f2543Smrg	    fprintf(stderr,"Unknown component \"%s\" ignored\n",tok);
318706f2543Smrg	}
319706f2543Smrg   }
320706f2543Smrg   if ((present&PART_MASK)==0) {
321706f2543Smrg	unsigned mask= PART_MASK;
322706f2543Smrg	ErrorF("Mapping needs at least one of ");
323706f2543Smrg	for (i=0; (i<MAX_WORDS); i++) {
324706f2543Smrg	    if ((1L<<i)&mask) {
325706f2543Smrg		mask&= ~(1L<<i);
326706f2543Smrg		if (mask)	DebugF("\"%s,\" ",cname[i]);
327706f2543Smrg		else		DebugF("or \"%s\"\n",cname[i]);
328706f2543Smrg	    }
329706f2543Smrg	}
330706f2543Smrg	DebugF("Illegal mapping ignored\n");
331706f2543Smrg	remap->num_remap= 0;
332706f2543Smrg	return;
333706f2543Smrg   }
334706f2543Smrg   if ((present&COMPONENT_MASK)==0) {
335706f2543Smrg	DebugF("Mapping needs at least one component\n");
336706f2543Smrg	DebugF("Illegal mapping ignored\n");
337706f2543Smrg	remap->num_remap= 0;
338706f2543Smrg	return;
339706f2543Smrg   }
340706f2543Smrg   remap->number++;
341706f2543Smrg   return;
342706f2543Smrg}
343706f2543Smrg
344706f2543Smrgstatic Bool
345706f2543SmrgMatchOneOf(char *wanted,char *vals_defined)
346706f2543Smrg{
347706f2543Smrgchar	*str,*next;
348706f2543Smrgint	want_len= strlen(wanted);
349706f2543Smrg
350706f2543Smrg    for (str=vals_defined,next=NULL;str!=NULL;str=next) {
351706f2543Smrg	int len;
352706f2543Smrg	next= strchr(str,',');
353706f2543Smrg	if (next) {
354706f2543Smrg	    len= next-str;
355706f2543Smrg	    next++;
356706f2543Smrg	}
357706f2543Smrg	else {
358706f2543Smrg	    len= strlen(str);
359706f2543Smrg	}
360706f2543Smrg	if ((len==want_len)&&(strncmp(wanted,str,len)==0))
361706f2543Smrg	    return TRUE;
362706f2543Smrg    }
363706f2543Smrg    return FALSE;
364706f2543Smrg}
365706f2543Smrg
366706f2543Smrg/***====================================================================***/
367706f2543Smrg
368706f2543Smrgstatic Bool
369706f2543SmrgCheckLine(	InputLine *		line,
370706f2543Smrg		RemapSpec *		remap,
371706f2543Smrg		XkbRF_RulePtr		rule,
372706f2543Smrg		XkbRF_GroupPtr		group)
373706f2543Smrg{
374706f2543Smrgchar *		str,*tok;
375706f2543Smrgregister int	nread, i;
376706f2543SmrgFileSpec	tmp;
377706f2543Smrg_Xstrtokparams	strtok_buf;
378706f2543SmrgBool 		append = FALSE;
379706f2543Smrg
380706f2543Smrg    if (line->line[0]=='!') {
381706f2543Smrg        if (line->line[1] == '$' ||
382706f2543Smrg            (line->line[1] == ' ' && line->line[2] == '$')) {
383706f2543Smrg            char *gname = strchr(line->line, '$');
384706f2543Smrg            char *words = strchr(gname, ' ');
385706f2543Smrg            if(!words)
386706f2543Smrg                return FALSE;
387706f2543Smrg            *words++ = '\0';
388706f2543Smrg            for (; *words; words++) {
389706f2543Smrg                if (*words != '=' && *words != ' ')
390706f2543Smrg                    break;
391706f2543Smrg            }
392706f2543Smrg            if (*words == '\0')
393706f2543Smrg                return FALSE;
394706f2543Smrg            group->name = _XkbDupString(gname);
395706f2543Smrg            group->words = _XkbDupString(words);
396706f2543Smrg            for (i = 1, words = group->words; *words; words++) {
397706f2543Smrg                 if ( *words == ' ') {
398706f2543Smrg                     *words++ = '\0';
399706f2543Smrg                     i++;
400706f2543Smrg                 }
401706f2543Smrg            }
402706f2543Smrg            group->number = i;
403706f2543Smrg            return TRUE;
404706f2543Smrg        } else {
405706f2543Smrg	    SetUpRemap(line,remap);
406706f2543Smrg	    return FALSE;
407706f2543Smrg        }
408706f2543Smrg    }
409706f2543Smrg
410706f2543Smrg    if (remap->num_remap==0) {
411706f2543Smrg	DebugF("Must have a mapping before first line of data\n");
412706f2543Smrg	DebugF("Illegal line of data ignored\n");
413706f2543Smrg	return FALSE;
414706f2543Smrg    }
415706f2543Smrg    memset((char *)&tmp, 0, sizeof(FileSpec));
416706f2543Smrg    str= line->line;
417706f2543Smrg    for (nread= 0;(tok=_XStrtok(str," ",strtok_buf))!=NULL;nread++) {
418706f2543Smrg	str= NULL;
419706f2543Smrg	if (strcmp(tok,"=")==0) {
420706f2543Smrg	    nread--;
421706f2543Smrg	    continue;
422706f2543Smrg	}
423706f2543Smrg	if (nread>remap->num_remap) {
424706f2543Smrg	    DebugF("Too many words on a line\n");
425706f2543Smrg	    DebugF("Extra word \"%s\" ignored\n",tok);
426706f2543Smrg	    continue;
427706f2543Smrg	}
428706f2543Smrg	tmp.name[remap->remap[nread].word]= tok;
429706f2543Smrg	if (*tok == '+' || *tok == '|')
430706f2543Smrg	    append = TRUE;
431706f2543Smrg    }
432706f2543Smrg    if (nread<remap->num_remap) {
433706f2543Smrg	DebugF("Too few words on a line: %s\n", line->line);
434706f2543Smrg	DebugF("line ignored\n");
435706f2543Smrg	return FALSE;
436706f2543Smrg    }
437706f2543Smrg
438706f2543Smrg    rule->flags= 0;
439706f2543Smrg    rule->number = remap->number;
440706f2543Smrg    if (tmp.name[OPTION])
441706f2543Smrg	 rule->flags|= XkbRF_Option;
442706f2543Smrg    else if (append)
443706f2543Smrg	 rule->flags|= XkbRF_Append;
444706f2543Smrg    else
445706f2543Smrg	 rule->flags|= XkbRF_Normal;
446706f2543Smrg    rule->model= _XkbDupString(tmp.name[MODEL]);
447706f2543Smrg    rule->layout= _XkbDupString(tmp.name[LAYOUT]);
448706f2543Smrg    rule->variant= _XkbDupString(tmp.name[VARIANT]);
449706f2543Smrg    rule->option= _XkbDupString(tmp.name[OPTION]);
450706f2543Smrg
451706f2543Smrg    rule->keycodes= _XkbDupString(tmp.name[KEYCODES]);
452706f2543Smrg    rule->symbols= _XkbDupString(tmp.name[SYMBOLS]);
453706f2543Smrg    rule->types= _XkbDupString(tmp.name[TYPES]);
454706f2543Smrg    rule->compat= _XkbDupString(tmp.name[COMPAT]);
455706f2543Smrg    rule->geometry= _XkbDupString(tmp.name[GEOMETRY]);
456706f2543Smrg
457706f2543Smrg    rule->layout_num = rule->variant_num = 0;
458706f2543Smrg    for (i = 0; i < nread; i++) {
459706f2543Smrg        if (remap->remap[i].index) {
460706f2543Smrg	    if (remap->remap[i].word == LAYOUT)
461706f2543Smrg	        rule->layout_num = remap->remap[i].index;
462706f2543Smrg	    if (remap->remap[i].word == VARIANT)
463706f2543Smrg	        rule->variant_num = remap->remap[i].index;
464706f2543Smrg        }
465706f2543Smrg    }
466706f2543Smrg    return TRUE;
467706f2543Smrg}
468706f2543Smrg
469706f2543Smrgstatic char *
470706f2543Smrg_Concat(char *str1,char *str2)
471706f2543Smrg{
472706f2543Smrgint len;
473706f2543Smrg
474706f2543Smrg    if ((!str1)||(!str2))
475706f2543Smrg	return str1;
476706f2543Smrg    len= strlen(str1)+strlen(str2)+1;
477706f2543Smrg    str1= realloc(str1,len * sizeof(char));
478706f2543Smrg    if (str1)
479706f2543Smrg	strcat(str1,str2);
480706f2543Smrg    return str1;
481706f2543Smrg}
482706f2543Smrg
483706f2543Smrgstatic void
484706f2543Smrgsqueeze_spaces(char *p1)
485706f2543Smrg{
486706f2543Smrg   char *p2;
487706f2543Smrg   for (p2 = p1; *p2; p2++) {
488706f2543Smrg       *p1 = *p2;
489706f2543Smrg       if (*p1 != ' ') p1++;
490706f2543Smrg   }
491706f2543Smrg   *p1 = '\0';
492706f2543Smrg}
493706f2543Smrg
494706f2543Smrgstatic Bool
495706f2543SmrgMakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs)
496706f2543Smrg{
497706f2543Smrg
498706f2543Smrg   memset((char *)mdefs, 0, sizeof(XkbRF_MultiDefsRec));
499706f2543Smrg   mdefs->model = defs->model;
500706f2543Smrg   mdefs->options = _XkbDupString(defs->options);
501706f2543Smrg   if (mdefs->options) squeeze_spaces(mdefs->options);
502706f2543Smrg
503706f2543Smrg   if (defs->layout) {
504706f2543Smrg       if (!strchr(defs->layout, ',')) {
505706f2543Smrg           mdefs->layout[0] = defs->layout;
506706f2543Smrg       } else {
507706f2543Smrg           char *p;
508706f2543Smrg           int i;
509706f2543Smrg           mdefs->layout[1] = _XkbDupString(defs->layout);
510706f2543Smrg	   if (mdefs->layout[1] == NULL)
511706f2543Smrg	      return FALSE;
512706f2543Smrg           squeeze_spaces(mdefs->layout[1]);
513706f2543Smrg           p = mdefs->layout[1];
514706f2543Smrg           for (i = 2; i <= XkbNumKbdGroups; i++) {
515706f2543Smrg              if ((p = strchr(p, ','))) {
516706f2543Smrg                 *p++ = '\0';
517706f2543Smrg                 mdefs->layout[i] = p;
518706f2543Smrg              } else {
519706f2543Smrg                 break;
520706f2543Smrg              }
521706f2543Smrg           }
522706f2543Smrg           if (p && (p = strchr(p, ',')))
523706f2543Smrg              *p = '\0';
524706f2543Smrg       }
525706f2543Smrg   }
526706f2543Smrg
527706f2543Smrg   if (defs->variant) {
528706f2543Smrg       if (!strchr(defs->variant, ',')) {
529706f2543Smrg           mdefs->variant[0] = defs->variant;
530706f2543Smrg       } else {
531706f2543Smrg           char *p;
532706f2543Smrg           int i;
533706f2543Smrg           mdefs->variant[1] = _XkbDupString(defs->variant);
534706f2543Smrg	   if (mdefs->variant[1] == NULL)
535706f2543Smrg	      return FALSE;
536706f2543Smrg           squeeze_spaces(mdefs->variant[1]);
537706f2543Smrg           p = mdefs->variant[1];
538706f2543Smrg           for (i = 2; i <= XkbNumKbdGroups; i++) {
539706f2543Smrg              if ((p = strchr(p, ','))) {
540706f2543Smrg                 *p++ = '\0';
541706f2543Smrg                 mdefs->variant[i] = p;
542706f2543Smrg              } else {
543706f2543Smrg                 break;
544706f2543Smrg              }
545706f2543Smrg           }
546706f2543Smrg           if (p && (p = strchr(p, ',')))
547706f2543Smrg              *p = '\0';
548706f2543Smrg       }
549706f2543Smrg   }
550706f2543Smrg   return TRUE;
551706f2543Smrg}
552706f2543Smrg
553706f2543Smrgstatic void
554706f2543SmrgFreeMultiDefs(XkbRF_MultiDefsPtr defs)
555706f2543Smrg{
556706f2543Smrg  free(defs->options);
557706f2543Smrg  free(defs->layout[1]);
558706f2543Smrg  free(defs->variant[1]);
559706f2543Smrg}
560706f2543Smrg
561706f2543Smrgstatic void
562706f2543SmrgApply(char *src, char **dst)
563706f2543Smrg{
564706f2543Smrg    if (src) {
565706f2543Smrg        if (*src == '+' || *src == '!') {
566706f2543Smrg	    *dst= _Concat(*dst, src);
567706f2543Smrg        } else {
568706f2543Smrg            if (*dst == NULL)
569706f2543Smrg	        *dst= _XkbDupString(src);
570706f2543Smrg        }
571706f2543Smrg    }
572706f2543Smrg}
573706f2543Smrg
574706f2543Smrgstatic void
575706f2543SmrgXkbRF_ApplyRule(	XkbRF_RulePtr 		rule,
576706f2543Smrg			XkbComponentNamesPtr	names)
577706f2543Smrg{
578706f2543Smrg    rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
579706f2543Smrg
580706f2543Smrg    Apply(rule->keycodes, &names->keycodes);
581706f2543Smrg    Apply(rule->symbols,  &names->symbols);
582706f2543Smrg    Apply(rule->types,    &names->types);
583706f2543Smrg    Apply(rule->compat,   &names->compat);
584706f2543Smrg    Apply(rule->geometry, &names->geometry);
585706f2543Smrg}
586706f2543Smrg
587706f2543Smrgstatic Bool
588706f2543SmrgCheckGroup(	XkbRF_RulesPtr          rules,
589706f2543Smrg		char * 			group_name,
590706f2543Smrg		char * 			name)
591706f2543Smrg{
592706f2543Smrg   int i;
593706f2543Smrg   char *p;
594706f2543Smrg   XkbRF_GroupPtr group;
595706f2543Smrg
596706f2543Smrg   for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
597706f2543Smrg       if (! strcmp(group->name, group_name)) {
598706f2543Smrg           break;
599706f2543Smrg       }
600706f2543Smrg   }
601706f2543Smrg   if (i == rules->num_groups)
602706f2543Smrg       return FALSE;
603706f2543Smrg   for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
604706f2543Smrg       if (! strcmp(p, name)) {
605706f2543Smrg           return TRUE;
606706f2543Smrg       }
607706f2543Smrg   }
608706f2543Smrg   return FALSE;
609706f2543Smrg}
610706f2543Smrg
611706f2543Smrgstatic int
612706f2543SmrgXkbRF_CheckApplyRule(	XkbRF_RulePtr 		rule,
613706f2543Smrg			XkbRF_MultiDefsPtr	mdefs,
614706f2543Smrg			XkbComponentNamesPtr	names,
615706f2543Smrg			XkbRF_RulesPtr          rules)
616706f2543Smrg{
617706f2543Smrg    Bool pending = FALSE;
618706f2543Smrg
619706f2543Smrg    if (rule->model != NULL) {
620706f2543Smrg        if(mdefs->model == NULL)
621706f2543Smrg            return 0;
622706f2543Smrg        if (strcmp(rule->model, "*") == 0) {
623706f2543Smrg            pending = TRUE;
624706f2543Smrg        } else {
625706f2543Smrg            if (rule->model[0] == '$') {
626706f2543Smrg               if (!CheckGroup(rules, rule->model, mdefs->model))
627706f2543Smrg                  return 0;
628706f2543Smrg            } else {
629706f2543Smrg	       if (strcmp(rule->model, mdefs->model) != 0)
630706f2543Smrg	          return 0;
631706f2543Smrg	    }
632706f2543Smrg	}
633706f2543Smrg    }
634706f2543Smrg    if (rule->option != NULL) {
635706f2543Smrg	if (mdefs->options == NULL)
636706f2543Smrg	    return 0;
637706f2543Smrg	if ((!MatchOneOf(rule->option,mdefs->options)))
638706f2543Smrg	    return 0;
639706f2543Smrg    }
640706f2543Smrg
641706f2543Smrg    if (rule->layout != NULL) {
642706f2543Smrg	if(mdefs->layout[rule->layout_num] == NULL ||
643706f2543Smrg	   *mdefs->layout[rule->layout_num] == '\0')
644706f2543Smrg	    return 0;
645706f2543Smrg        if (strcmp(rule->layout, "*") == 0) {
646706f2543Smrg            pending = TRUE;
647706f2543Smrg        } else {
648706f2543Smrg            if (rule->layout[0] == '$') {
649706f2543Smrg               if (!CheckGroup(rules, rule->layout,
650706f2543Smrg                               mdefs->layout[rule->layout_num]))
651706f2543Smrg                  return 0;
652706f2543Smrg	    } else {
653706f2543Smrg	       if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
654706f2543Smrg	           return 0;
655706f2543Smrg	    }
656706f2543Smrg	}
657706f2543Smrg    }
658706f2543Smrg    if (rule->variant != NULL) {
659706f2543Smrg	if (mdefs->variant[rule->variant_num] == NULL ||
660706f2543Smrg	    *mdefs->variant[rule->variant_num] == '\0')
661706f2543Smrg	    return 0;
662706f2543Smrg        if (strcmp(rule->variant, "*") == 0) {
663706f2543Smrg            pending = TRUE;
664706f2543Smrg        } else {
665706f2543Smrg            if (rule->variant[0] == '$') {
666706f2543Smrg               if (!CheckGroup(rules, rule->variant,
667706f2543Smrg                               mdefs->variant[rule->variant_num]))
668706f2543Smrg                  return 0;
669706f2543Smrg            } else {
670706f2543Smrg	       if (strcmp(rule->variant,
671706f2543Smrg                          mdefs->variant[rule->variant_num]) != 0)
672706f2543Smrg	           return 0;
673706f2543Smrg	    }
674706f2543Smrg	}
675706f2543Smrg    }
676706f2543Smrg    if (pending) {
677706f2543Smrg        rule->flags|= XkbRF_PendingMatch;
678706f2543Smrg	return rule->number;
679706f2543Smrg    }
680706f2543Smrg    /* exact match, apply it now */
681706f2543Smrg    XkbRF_ApplyRule(rule,names);
682706f2543Smrg    return rule->number;
683706f2543Smrg}
684706f2543Smrg
685706f2543Smrgstatic void
686706f2543SmrgXkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
687706f2543Smrg{
688706f2543Smrgregister int 	i;
689706f2543SmrgXkbRF_RulePtr	rule;
690706f2543Smrg
691706f2543Smrg    for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
692706f2543Smrg	rule->flags&= ~XkbRF_PendingMatch;
693706f2543Smrg    }
694706f2543Smrg}
695706f2543Smrg
696706f2543Smrgstatic void
697706f2543SmrgXkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules,XkbComponentNamesPtr names)
698706f2543Smrg{
699706f2543Smrgint		i;
700706f2543SmrgXkbRF_RulePtr	rule;
701706f2543Smrg
702706f2543Smrg    for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
703706f2543Smrg	if ((rule->flags&XkbRF_PendingMatch)==0)
704706f2543Smrg	    continue;
705706f2543Smrg	XkbRF_ApplyRule(rule,names);
706706f2543Smrg    }
707706f2543Smrg}
708706f2543Smrg
709706f2543Smrgstatic void
710706f2543SmrgXkbRF_CheckApplyRules(	XkbRF_RulesPtr 		rules,
711706f2543Smrg			XkbRF_MultiDefsPtr	mdefs,
712706f2543Smrg			XkbComponentNamesPtr	names,
713706f2543Smrg			int			flags)
714706f2543Smrg{
715706f2543Smrgint		i;
716706f2543SmrgXkbRF_RulePtr	rule;
717706f2543Smrgint		skip;
718706f2543Smrg
719706f2543Smrg    for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
720706f2543Smrg	if ((rule->flags & flags) != flags)
721706f2543Smrg	    continue;
722706f2543Smrg	skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
723706f2543Smrg	if (skip && !(flags & XkbRF_Option)) {
724706f2543Smrg	    for ( ;(i < rules->num_rules) && (rule->number == skip);
725706f2543Smrg		  rule++, i++);
726706f2543Smrg	    rule--; i--;
727706f2543Smrg	}
728706f2543Smrg    }
729706f2543Smrg}
730706f2543Smrg
731706f2543Smrg/***====================================================================***/
732706f2543Smrg
733706f2543Smrgstatic char *
734706f2543SmrgXkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
735706f2543Smrg{
736706f2543Smrgchar 	*str, *outstr, *orig, *var;
737706f2543Smrgint	len, ndx;
738706f2543Smrg
739706f2543Smrg    orig= name;
740706f2543Smrg    str= index(name,'%');
741706f2543Smrg    if (str==NULL)
742706f2543Smrg	return name;
743706f2543Smrg    len= strlen(name);
744706f2543Smrg    while (str!=NULL) {
745706f2543Smrg	char pfx= str[1];
746706f2543Smrg	int   extra_len= 0;
747706f2543Smrg	if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
748706f2543Smrg	    extra_len= 1;
749706f2543Smrg	    str++;
750706f2543Smrg	}
751706f2543Smrg	else if (pfx=='(') {
752706f2543Smrg	    extra_len= 2;
753706f2543Smrg	    str++;
754706f2543Smrg	}
755706f2543Smrg	var = str + 1;
756706f2543Smrg	str = get_index(var + 1, &ndx);
757706f2543Smrg	if (ndx == -1) {
758706f2543Smrg	    str = index(str,'%');
759706f2543Smrg	    continue;
760706f2543Smrg        }
761706f2543Smrg	if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
762706f2543Smrg	    len+= strlen(mdefs->layout[ndx])+extra_len;
763706f2543Smrg	else if ((*var=='m')&&mdefs->model)
764706f2543Smrg	    len+= strlen(mdefs->model)+extra_len;
765706f2543Smrg	else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
766706f2543Smrg	    len+= strlen(mdefs->variant[ndx])+extra_len;
767706f2543Smrg	if ((pfx=='(')&&(*str==')')) {
768706f2543Smrg	    str++;
769706f2543Smrg	}
770706f2543Smrg	str= index(&str[0],'%');
771706f2543Smrg    }
772706f2543Smrg    name= malloc(len+1);
773706f2543Smrg    str= orig;
774706f2543Smrg    outstr= name;
775706f2543Smrg    while (*str!='\0') {
776706f2543Smrg	if (str[0]=='%') {
777706f2543Smrg	    char pfx,sfx;
778706f2543Smrg	    str++;
779706f2543Smrg	    pfx= str[0];
780706f2543Smrg	    sfx= '\0';
781706f2543Smrg	    if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
782706f2543Smrg		str++;
783706f2543Smrg	    }
784706f2543Smrg	    else if (pfx=='(') {
785706f2543Smrg		sfx= ')';
786706f2543Smrg		str++;
787706f2543Smrg	    }
788706f2543Smrg	    else pfx= '\0';
789706f2543Smrg
790706f2543Smrg	    var = str;
791706f2543Smrg	    str = get_index(var + 1, &ndx);
792706f2543Smrg	    if (ndx == -1) {
793706f2543Smrg	        continue;
794706f2543Smrg            }
795706f2543Smrg	    if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
796706f2543Smrg		if (pfx) *outstr++= pfx;
797706f2543Smrg		strcpy(outstr,mdefs->layout[ndx]);
798706f2543Smrg		outstr+= strlen(mdefs->layout[ndx]);
799706f2543Smrg		if (sfx) *outstr++= sfx;
800706f2543Smrg	    }
801706f2543Smrg	    else if ((*var=='m')&&(mdefs->model)) {
802706f2543Smrg		if (pfx) *outstr++= pfx;
803706f2543Smrg		strcpy(outstr,mdefs->model);
804706f2543Smrg		outstr+= strlen(mdefs->model);
805706f2543Smrg		if (sfx) *outstr++= sfx;
806706f2543Smrg	    }
807706f2543Smrg	    else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
808706f2543Smrg		if (pfx) *outstr++= pfx;
809706f2543Smrg		strcpy(outstr,mdefs->variant[ndx]);
810706f2543Smrg		outstr+= strlen(mdefs->variant[ndx]);
811706f2543Smrg		if (sfx) *outstr++= sfx;
812706f2543Smrg	    }
813706f2543Smrg	    if ((pfx=='(')&&(*str==')'))
814706f2543Smrg		str++;
815706f2543Smrg	}
816706f2543Smrg	else {
817706f2543Smrg	    *outstr++= *str++;
818706f2543Smrg	}
819706f2543Smrg    }
820706f2543Smrg    *outstr++= '\0';
821706f2543Smrg    if (orig!=name)
822706f2543Smrg	free(orig);
823706f2543Smrg    return name;
824706f2543Smrg}
825706f2543Smrg
826706f2543Smrg/***====================================================================***/
827706f2543Smrg
828706f2543SmrgBool
829706f2543SmrgXkbRF_GetComponents(	XkbRF_RulesPtr		rules,
830706f2543Smrg			XkbRF_VarDefsPtr	defs,
831706f2543Smrg			XkbComponentNamesPtr	names)
832706f2543Smrg{
833706f2543Smrg    XkbRF_MultiDefsRec mdefs;
834706f2543Smrg
835706f2543Smrg    MakeMultiDefs(&mdefs, defs);
836706f2543Smrg
837706f2543Smrg    memset((char *)names, 0, sizeof(XkbComponentNamesRec));
838706f2543Smrg    XkbRF_ClearPartialMatches(rules);
839706f2543Smrg    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
840706f2543Smrg    XkbRF_ApplyPartialMatches(rules, names);
841706f2543Smrg    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
842706f2543Smrg    XkbRF_ApplyPartialMatches(rules, names);
843706f2543Smrg    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
844706f2543Smrg
845706f2543Smrg    if (names->keycodes)
846706f2543Smrg	names->keycodes= XkbRF_SubstituteVars(names->keycodes, &mdefs);
847706f2543Smrg    if (names->symbols)
848706f2543Smrg	names->symbols=	XkbRF_SubstituteVars(names->symbols, &mdefs);
849706f2543Smrg    if (names->types)
850706f2543Smrg	names->types= XkbRF_SubstituteVars(names->types, &mdefs);
851706f2543Smrg    if (names->compat)
852706f2543Smrg	names->compat= XkbRF_SubstituteVars(names->compat, &mdefs);
853706f2543Smrg    if (names->geometry)
854706f2543Smrg	names->geometry= XkbRF_SubstituteVars(names->geometry, &mdefs);
855706f2543Smrg
856706f2543Smrg    FreeMultiDefs(&mdefs);
857706f2543Smrg    return (names->keycodes && names->symbols && names->types &&
858706f2543Smrg		names->compat && names->geometry);
859706f2543Smrg}
860706f2543Smrg
861706f2543Smrgstatic XkbRF_RulePtr
862706f2543SmrgXkbRF_AddRule(XkbRF_RulesPtr	rules)
863706f2543Smrg{
864706f2543Smrg    if (rules->sz_rules<1) {
865706f2543Smrg	rules->sz_rules= 16;
866706f2543Smrg	rules->num_rules= 0;
867706f2543Smrg	rules->rules= calloc(rules->sz_rules, sizeof(XkbRF_RuleRec));
868706f2543Smrg    }
869706f2543Smrg    else if (rules->num_rules>=rules->sz_rules) {
870706f2543Smrg	rules->sz_rules*= 2;
871706f2543Smrg	rules->rules= realloc(rules->rules,
872706f2543Smrg				rules->sz_rules * sizeof(XkbRF_RuleRec));
873706f2543Smrg    }
874706f2543Smrg    if (!rules->rules) {
875706f2543Smrg	rules->sz_rules= rules->num_rules= 0;
876706f2543Smrg	DebugF("Allocation failure in XkbRF_AddRule\n");
877706f2543Smrg	return NULL;
878706f2543Smrg    }
879706f2543Smrg    memset((char *)&rules->rules[rules->num_rules], 0, sizeof(XkbRF_RuleRec));
880706f2543Smrg    return &rules->rules[rules->num_rules++];
881706f2543Smrg}
882706f2543Smrg
883706f2543Smrgstatic XkbRF_GroupPtr
884706f2543SmrgXkbRF_AddGroup(XkbRF_RulesPtr	rules)
885706f2543Smrg{
886706f2543Smrg    if (rules->sz_groups<1) {
887706f2543Smrg	rules->sz_groups= 16;
888706f2543Smrg	rules->num_groups= 0;
889706f2543Smrg	rules->groups= calloc(rules->sz_groups, sizeof(XkbRF_GroupRec));
890706f2543Smrg    }
891706f2543Smrg    else if (rules->num_groups >= rules->sz_groups) {
892706f2543Smrg	rules->sz_groups *= 2;
893706f2543Smrg	rules->groups= realloc(rules->groups,
894706f2543Smrg				rules->sz_groups * sizeof(XkbRF_GroupRec));
895706f2543Smrg    }
896706f2543Smrg    if (!rules->groups) {
897706f2543Smrg	rules->sz_groups= rules->num_groups= 0;
898706f2543Smrg	return NULL;
899706f2543Smrg    }
900706f2543Smrg
901706f2543Smrg    memset((char *)&rules->groups[rules->num_groups], 0, sizeof(XkbRF_GroupRec));
902706f2543Smrg    return &rules->groups[rules->num_groups++];
903706f2543Smrg}
904706f2543Smrg
905706f2543SmrgBool
906706f2543SmrgXkbRF_LoadRules(FILE *file, XkbRF_RulesPtr rules)
907706f2543Smrg{
908706f2543SmrgInputLine	line;
909706f2543SmrgRemapSpec	remap;
910706f2543SmrgXkbRF_RuleRec	trule,*rule;
911706f2543SmrgXkbRF_GroupRec  tgroup,*group;
912706f2543Smrg
913706f2543Smrg    if (!(rules && file))
914706f2543Smrg	return FALSE;
915706f2543Smrg    memset((char *)&remap, 0, sizeof(RemapSpec));
916706f2543Smrg    memset((char *)&tgroup, 0, sizeof(XkbRF_GroupRec));
917706f2543Smrg    InitInputLine(&line);
918706f2543Smrg    while (GetInputLine(file,&line,TRUE)) {
919706f2543Smrg	if (CheckLine(&line,&remap,&trule,&tgroup)) {
920706f2543Smrg            if (tgroup.number) {
921706f2543Smrg	        if ((group= XkbRF_AddGroup(rules))!=NULL) {
922706f2543Smrg		    *group= tgroup;
923706f2543Smrg		    memset((char *)&tgroup, 0, sizeof(XkbRF_GroupRec));
924706f2543Smrg	        }
925706f2543Smrg	    } else {
926706f2543Smrg	        if ((rule= XkbRF_AddRule(rules))!=NULL) {
927706f2543Smrg		    *rule= trule;
928706f2543Smrg		    memset((char *)&trule, 0, sizeof(XkbRF_RuleRec));
929706f2543Smrg	        }
930706f2543Smrg	    }
931706f2543Smrg	}
932706f2543Smrg	line.num_line= 0;
933706f2543Smrg    }
934706f2543Smrg    FreeInputLine(&line);
935706f2543Smrg    return TRUE;
936706f2543Smrg}
937706f2543Smrg
938706f2543SmrgBool
939706f2543SmrgXkbRF_LoadRulesByName(char *base,char *locale,XkbRF_RulesPtr rules)
940706f2543Smrg{
941706f2543SmrgFILE *		file;
942706f2543Smrgchar		buf[PATH_MAX];
943706f2543SmrgBool		ok;
944706f2543Smrg
945706f2543Smrg    if ((!base)||(!rules))
946706f2543Smrg	return FALSE;
947706f2543Smrg    if (locale) {
948706f2543Smrg	if (strlen(base)+strlen(locale)+2 > PATH_MAX)
949706f2543Smrg	    return FALSE;
950706f2543Smrg	sprintf(buf,"%s-%s", base, locale);
951706f2543Smrg    }
952706f2543Smrg    else {
953706f2543Smrg	if (strlen(base)+1 > PATH_MAX)
954706f2543Smrg	    return FALSE;
955706f2543Smrg	strcpy(buf,base);
956706f2543Smrg    }
957706f2543Smrg
958706f2543Smrg    file= fopen(buf, "r");
959706f2543Smrg    if ((!file)&&(locale)) { /* fallback if locale was specified */
960706f2543Smrg	strcpy(buf,base);
961706f2543Smrg	file= fopen(buf, "r");
962706f2543Smrg    }
963706f2543Smrg    if (!file)
964706f2543Smrg	return FALSE;
965706f2543Smrg    ok= XkbRF_LoadRules(file,rules);
966706f2543Smrg    fclose(file);
967706f2543Smrg    return ok;
968706f2543Smrg}
969706f2543Smrg
970706f2543Smrg/***====================================================================***/
971706f2543Smrg
972706f2543SmrgXkbRF_RulesPtr
973706f2543SmrgXkbRF_Create(void)
974706f2543Smrg{
975706f2543Smrg    return calloc(1, sizeof( XkbRF_RulesRec));
976706f2543Smrg}
977706f2543Smrg
978706f2543Smrg/***====================================================================***/
979706f2543Smrg
980706f2543Smrgvoid
981706f2543SmrgXkbRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
982706f2543Smrg{
983706f2543Smrgint		i;
984706f2543SmrgXkbRF_RulePtr	rule;
985706f2543SmrgXkbRF_GroupPtr	group;
986706f2543Smrg
987706f2543Smrg    if (!rules)
988706f2543Smrg	return;
989706f2543Smrg    if (rules->rules) {
990706f2543Smrg	for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
991706f2543Smrg	    free(rule->model);
992706f2543Smrg	    free(rule->layout);
993706f2543Smrg	    free(rule->variant);
994706f2543Smrg	    free(rule->option);
995706f2543Smrg	    free(rule->keycodes);
996706f2543Smrg	    free(rule->symbols);
997706f2543Smrg	    free(rule->types);
998706f2543Smrg	    free(rule->compat);
999706f2543Smrg	    free(rule->geometry);
1000706f2543Smrg	    memset((char *)rule, 0, sizeof(XkbRF_RuleRec));
1001706f2543Smrg	}
1002706f2543Smrg	free(rules->rules);
1003706f2543Smrg	rules->num_rules= rules->sz_rules= 0;
1004706f2543Smrg	rules->rules= NULL;
1005706f2543Smrg    }
1006706f2543Smrg
1007706f2543Smrg    if (rules->groups) {
1008706f2543Smrg	for (i=0, group=rules->groups;i<rules->num_groups;i++,group++) {
1009706f2543Smrg	    free(group->name);
1010706f2543Smrg	    free(group->words);
1011706f2543Smrg	}
1012706f2543Smrg	free(rules->groups);
1013706f2543Smrg	rules->num_groups= 0;
1014706f2543Smrg	rules->groups= NULL;
1015706f2543Smrg    }
1016706f2543Smrg    if (freeRules)
1017706f2543Smrg	free(rules);
1018706f2543Smrg    return;
1019706f2543Smrg}
1020