maprules.c revision 706f2543
1/************************************************************
2 Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
3
4 Permission to use, copy, modify, and distribute this
5 software and its documentation for any purpose and without
6 fee is hereby granted, provided that the above copyright
7 notice appear in all copies and that both that copyright
8 notice and this permission notice appear in supporting
9 documentation, and that the name of Silicon Graphics not be
10 used in advertising or publicity pertaining to distribution
11 of the software without specific prior written permission.
12 Silicon Graphics makes no representation about the suitability
13 of this software for any purpose. It is provided "as is"
14 without any express or implied warranty.
15
16 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23 THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25 ********************************************************/
26
27#ifdef HAVE_DIX_CONFIG_H
28#include <dix-config.h>
29#endif
30
31#include <stdio.h>
32#include <ctype.h>
33#include <stdlib.h>
34
35#define X_INCLUDE_STRING_H
36#define XOS_USE_NO_LOCKING
37#include <X11/Xos_r.h>
38
39#include <X11/Xproto.h>
40#include <X11/X.h>
41#include <X11/Xos.h>
42#include <X11/Xfuncs.h>
43#include <X11/Xatom.h>
44#include <X11/keysym.h>
45#include "misc.h"
46#include "inputstr.h"
47#include "dix.h"
48#include "os.h"
49#include "xkbstr.h"
50#define XKBSRV_NEED_FILE_FUNCS
51#include <xkbsrv.h>
52
53/***====================================================================***/
54
55
56
57#define DFLT_LINE_SIZE	128
58
59typedef struct {
60	int	line_num;
61	int	sz_line;
62	int	num_line;
63	char	buf[DFLT_LINE_SIZE];
64	char *	line;
65} InputLine;
66
67static void
68InitInputLine(InputLine *line)
69{
70    line->line_num= 1;
71    line->num_line= 0;
72    line->sz_line= DFLT_LINE_SIZE;
73    line->line=	line->buf;
74    return;
75}
76
77static void
78FreeInputLine(InputLine *line)
79{
80    if (line->line!=line->buf)
81	free(line->line);
82    line->line_num= 1;
83    line->num_line= 0;
84    line->sz_line= DFLT_LINE_SIZE;
85    line->line= line->buf;
86    return;
87}
88
89static int
90InputLineAddChar(InputLine *line,int ch)
91{
92    if (line->num_line>=line->sz_line) {
93	if (line->line==line->buf) {
94	    line->line= malloc(line->sz_line*2);
95	    memcpy(line->line,line->buf,line->sz_line);
96	}
97	else {
98	    line->line= realloc((char *)line->line,line->sz_line*2);
99	}
100	line->sz_line*= 2;
101    }
102    line->line[line->num_line++]= ch;
103    return ch;
104}
105
106#define	ADD_CHAR(l,c)	((l)->num_line<(l)->sz_line?\
107				(int)((l)->line[(l)->num_line++]= (c)):\
108				InputLineAddChar(l,c))
109
110static Bool
111GetInputLine(FILE *file,InputLine *line,Bool checkbang)
112{
113int	ch;
114Bool	endOfFile,spacePending,slashPending,inComment;
115
116     endOfFile= FALSE;
117     while ((!endOfFile)&&(line->num_line==0)) {
118	spacePending= slashPending= inComment= FALSE;
119	while (((ch=getc(file))!='\n')&&(ch!=EOF)) {
120	    if (ch=='\\') {
121		if ((ch=getc(file))==EOF)
122		    break;
123		if (ch=='\n') {
124		    inComment= FALSE;
125		    ch= ' ';
126		    line->line_num++;
127		}
128	    }
129	    if (inComment)
130		continue;
131	    if (ch=='/') {
132		if (slashPending) {
133		    inComment= TRUE;
134		    slashPending= FALSE;
135		}
136		else {
137		    slashPending= TRUE;
138		}
139		continue;
140	    }
141	    else if (slashPending) {
142		if (spacePending) {
143		    ADD_CHAR(line,' ');
144		    spacePending= FALSE;
145		}
146		ADD_CHAR(line,'/');
147		slashPending= FALSE;
148	    }
149	    if (isspace(ch)) {
150		while (isspace(ch)&&(ch!='\n')&&(ch!=EOF)) {
151		    ch= getc(file);
152		}
153		if (ch==EOF)
154		    break;
155		if ((ch!='\n')&&(line->num_line>0))
156		    spacePending= TRUE;
157		ungetc(ch,file);
158	    }
159	    else {
160		if (spacePending) {
161		    ADD_CHAR(line,' ');
162		    spacePending= FALSE;
163		}
164		if (checkbang && ch=='!') {
165		    if (line->num_line!=0) {
166			DebugF("The '!' legal only at start of line\n");
167			DebugF("Line containing '!' ignored\n");
168			line->num_line= 0;
169			inComment= 0;
170			break;
171		    }
172
173		}
174		ADD_CHAR(line,ch);
175	    }
176	}
177	if (ch==EOF)
178	     endOfFile= TRUE;
179/*	else line->num_line++;*/
180     }
181     if ((line->num_line==0)&&(endOfFile))
182	return FALSE;
183      ADD_CHAR(line,'\0');
184      return TRUE;
185}
186
187/***====================================================================***/
188
189#define	MODEL		0
190#define	LAYOUT		1
191#define	VARIANT		2
192#define	OPTION		3
193#define	KEYCODES	4
194#define SYMBOLS		5
195#define	TYPES		6
196#define	COMPAT		7
197#define	GEOMETRY	8
198#define	MAX_WORDS	9
199
200#define	PART_MASK	0x000F
201#define	COMPONENT_MASK	0x03F0
202
203static	char *	cname[MAX_WORDS] = {
204	"model", "layout", "variant", "option",
205	"keycodes", "symbols", "types", "compat", "geometry"
206};
207
208typedef	struct _RemapSpec {
209	int			number;
210	int			num_remap;
211	struct	{
212		int	word;
213		int	index;
214                }		remap[MAX_WORDS];
215} RemapSpec;
216
217typedef struct _FileSpec {
218	char *			name[MAX_WORDS];
219	struct _FileSpec *	pending;
220} FileSpec;
221
222typedef struct {
223	char *			model;
224	char *			layout[XkbNumKbdGroups+1];
225	char *			variant[XkbNumKbdGroups+1];
226	char *			options;
227} XkbRF_MultiDefsRec, *XkbRF_MultiDefsPtr;
228
229#define NDX_BUFF_SIZE	4
230
231/***====================================================================***/
232
233static char*
234get_index(char *str, int *ndx)
235{
236   char ndx_buf[NDX_BUFF_SIZE];
237   char *end;
238
239   if (*str != '[') {
240       *ndx = 0;
241       return str;
242   }
243   str++;
244   end = strchr(str, ']');
245   if (end == NULL) {
246       *ndx = -1;
247       return str - 1;
248   }
249   if ( (end - str) >= NDX_BUFF_SIZE) {
250       *ndx = -1;
251       return end + 1;
252   }
253   strncpy(ndx_buf, str, end - str);
254   ndx_buf[end - str] = '\0';
255   *ndx = atoi(ndx_buf);
256   return end + 1;
257}
258
259static void
260SetUpRemap(InputLine *line,RemapSpec *remap)
261{
262char *		tok,*str;
263unsigned	present, l_ndx_present, v_ndx_present;
264register int	i;
265int		len, ndx;
266_Xstrtokparams	strtok_buf;
267Bool		found;
268
269
270   l_ndx_present = v_ndx_present = present= 0;
271   str= &line->line[1];
272   len = remap->number;
273   memset((char *)remap, 0, sizeof(RemapSpec));
274   remap->number = len;
275   while ((tok=_XStrtok(str," ",strtok_buf))!=NULL) {
276	found= FALSE;
277	str= NULL;
278	if (strcmp(tok,"=")==0)
279	    continue;
280	for (i=0;i<MAX_WORDS;i++) {
281            len = strlen(cname[i]);
282	    if (strncmp(cname[i],tok,len)==0) {
283		if(strlen(tok) > len) {
284		    char *end = get_index(tok+len, &ndx);
285		    if ((i != LAYOUT && i != VARIANT) ||
286			*end != '\0' || ndx == -1)
287		        break;
288		     if (ndx < 1 || ndx > XkbNumKbdGroups) {
289		        DebugF("Illegal %s index: %d\n", cname[i], ndx);
290		        DebugF("Index must be in range 1..%d\n",
291				   XkbNumKbdGroups);
292			break;
293		     }
294                } else {
295		    ndx = 0;
296                }
297		found= TRUE;
298		if (present&(1<<i)) {
299		    if ((i == LAYOUT && l_ndx_present&(1<<ndx)) ||
300			(i == VARIANT && v_ndx_present&(1<<ndx)) ) {
301		        DebugF("Component \"%s\" listed twice\n",tok);
302		        DebugF("Second definition ignored\n");
303		        break;
304		    }
305		}
306		present |= (1<<i);
307                if (i == LAYOUT)
308                    l_ndx_present |= 1 << ndx;
309                if (i == VARIANT)
310                    v_ndx_present |= 1 << ndx;
311		remap->remap[remap->num_remap].word= i;
312		remap->remap[remap->num_remap++].index= ndx;
313		break;
314	    }
315	}
316	if (!found) {
317	    fprintf(stderr,"Unknown component \"%s\" ignored\n",tok);
318	}
319   }
320   if ((present&PART_MASK)==0) {
321	unsigned mask= PART_MASK;
322	ErrorF("Mapping needs at least one of ");
323	for (i=0; (i<MAX_WORDS); i++) {
324	    if ((1L<<i)&mask) {
325		mask&= ~(1L<<i);
326		if (mask)	DebugF("\"%s,\" ",cname[i]);
327		else		DebugF("or \"%s\"\n",cname[i]);
328	    }
329	}
330	DebugF("Illegal mapping ignored\n");
331	remap->num_remap= 0;
332	return;
333   }
334   if ((present&COMPONENT_MASK)==0) {
335	DebugF("Mapping needs at least one component\n");
336	DebugF("Illegal mapping ignored\n");
337	remap->num_remap= 0;
338	return;
339   }
340   remap->number++;
341   return;
342}
343
344static Bool
345MatchOneOf(char *wanted,char *vals_defined)
346{
347char	*str,*next;
348int	want_len= strlen(wanted);
349
350    for (str=vals_defined,next=NULL;str!=NULL;str=next) {
351	int len;
352	next= strchr(str,',');
353	if (next) {
354	    len= next-str;
355	    next++;
356	}
357	else {
358	    len= strlen(str);
359	}
360	if ((len==want_len)&&(strncmp(wanted,str,len)==0))
361	    return TRUE;
362    }
363    return FALSE;
364}
365
366/***====================================================================***/
367
368static Bool
369CheckLine(	InputLine *		line,
370		RemapSpec *		remap,
371		XkbRF_RulePtr		rule,
372		XkbRF_GroupPtr		group)
373{
374char *		str,*tok;
375register int	nread, i;
376FileSpec	tmp;
377_Xstrtokparams	strtok_buf;
378Bool 		append = FALSE;
379
380    if (line->line[0]=='!') {
381        if (line->line[1] == '$' ||
382            (line->line[1] == ' ' && line->line[2] == '$')) {
383            char *gname = strchr(line->line, '$');
384            char *words = strchr(gname, ' ');
385            if(!words)
386                return FALSE;
387            *words++ = '\0';
388            for (; *words; words++) {
389                if (*words != '=' && *words != ' ')
390                    break;
391            }
392            if (*words == '\0')
393                return FALSE;
394            group->name = _XkbDupString(gname);
395            group->words = _XkbDupString(words);
396            for (i = 1, words = group->words; *words; words++) {
397                 if ( *words == ' ') {
398                     *words++ = '\0';
399                     i++;
400                 }
401            }
402            group->number = i;
403            return TRUE;
404        } else {
405	    SetUpRemap(line,remap);
406	    return FALSE;
407        }
408    }
409
410    if (remap->num_remap==0) {
411	DebugF("Must have a mapping before first line of data\n");
412	DebugF("Illegal line of data ignored\n");
413	return FALSE;
414    }
415    memset((char *)&tmp, 0, sizeof(FileSpec));
416    str= line->line;
417    for (nread= 0;(tok=_XStrtok(str," ",strtok_buf))!=NULL;nread++) {
418	str= NULL;
419	if (strcmp(tok,"=")==0) {
420	    nread--;
421	    continue;
422	}
423	if (nread>remap->num_remap) {
424	    DebugF("Too many words on a line\n");
425	    DebugF("Extra word \"%s\" ignored\n",tok);
426	    continue;
427	}
428	tmp.name[remap->remap[nread].word]= tok;
429	if (*tok == '+' || *tok == '|')
430	    append = TRUE;
431    }
432    if (nread<remap->num_remap) {
433	DebugF("Too few words on a line: %s\n", line->line);
434	DebugF("line ignored\n");
435	return FALSE;
436    }
437
438    rule->flags= 0;
439    rule->number = remap->number;
440    if (tmp.name[OPTION])
441	 rule->flags|= XkbRF_Option;
442    else if (append)
443	 rule->flags|= XkbRF_Append;
444    else
445	 rule->flags|= XkbRF_Normal;
446    rule->model= _XkbDupString(tmp.name[MODEL]);
447    rule->layout= _XkbDupString(tmp.name[LAYOUT]);
448    rule->variant= _XkbDupString(tmp.name[VARIANT]);
449    rule->option= _XkbDupString(tmp.name[OPTION]);
450
451    rule->keycodes= _XkbDupString(tmp.name[KEYCODES]);
452    rule->symbols= _XkbDupString(tmp.name[SYMBOLS]);
453    rule->types= _XkbDupString(tmp.name[TYPES]);
454    rule->compat= _XkbDupString(tmp.name[COMPAT]);
455    rule->geometry= _XkbDupString(tmp.name[GEOMETRY]);
456
457    rule->layout_num = rule->variant_num = 0;
458    for (i = 0; i < nread; i++) {
459        if (remap->remap[i].index) {
460	    if (remap->remap[i].word == LAYOUT)
461	        rule->layout_num = remap->remap[i].index;
462	    if (remap->remap[i].word == VARIANT)
463	        rule->variant_num = remap->remap[i].index;
464        }
465    }
466    return TRUE;
467}
468
469static char *
470_Concat(char *str1,char *str2)
471{
472int len;
473
474    if ((!str1)||(!str2))
475	return str1;
476    len= strlen(str1)+strlen(str2)+1;
477    str1= realloc(str1,len * sizeof(char));
478    if (str1)
479	strcat(str1,str2);
480    return str1;
481}
482
483static void
484squeeze_spaces(char *p1)
485{
486   char *p2;
487   for (p2 = p1; *p2; p2++) {
488       *p1 = *p2;
489       if (*p1 != ' ') p1++;
490   }
491   *p1 = '\0';
492}
493
494static Bool
495MakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs)
496{
497
498   memset((char *)mdefs, 0, sizeof(XkbRF_MultiDefsRec));
499   mdefs->model = defs->model;
500   mdefs->options = _XkbDupString(defs->options);
501   if (mdefs->options) squeeze_spaces(mdefs->options);
502
503   if (defs->layout) {
504       if (!strchr(defs->layout, ',')) {
505           mdefs->layout[0] = defs->layout;
506       } else {
507           char *p;
508           int i;
509           mdefs->layout[1] = _XkbDupString(defs->layout);
510	   if (mdefs->layout[1] == NULL)
511	      return FALSE;
512           squeeze_spaces(mdefs->layout[1]);
513           p = mdefs->layout[1];
514           for (i = 2; i <= XkbNumKbdGroups; i++) {
515              if ((p = strchr(p, ','))) {
516                 *p++ = '\0';
517                 mdefs->layout[i] = p;
518              } else {
519                 break;
520              }
521           }
522           if (p && (p = strchr(p, ',')))
523              *p = '\0';
524       }
525   }
526
527   if (defs->variant) {
528       if (!strchr(defs->variant, ',')) {
529           mdefs->variant[0] = defs->variant;
530       } else {
531           char *p;
532           int i;
533           mdefs->variant[1] = _XkbDupString(defs->variant);
534	   if (mdefs->variant[1] == NULL)
535	      return FALSE;
536           squeeze_spaces(mdefs->variant[1]);
537           p = mdefs->variant[1];
538           for (i = 2; i <= XkbNumKbdGroups; i++) {
539              if ((p = strchr(p, ','))) {
540                 *p++ = '\0';
541                 mdefs->variant[i] = p;
542              } else {
543                 break;
544              }
545           }
546           if (p && (p = strchr(p, ',')))
547              *p = '\0';
548       }
549   }
550   return TRUE;
551}
552
553static void
554FreeMultiDefs(XkbRF_MultiDefsPtr defs)
555{
556  free(defs->options);
557  free(defs->layout[1]);
558  free(defs->variant[1]);
559}
560
561static void
562Apply(char *src, char **dst)
563{
564    if (src) {
565        if (*src == '+' || *src == '!') {
566	    *dst= _Concat(*dst, src);
567        } else {
568            if (*dst == NULL)
569	        *dst= _XkbDupString(src);
570        }
571    }
572}
573
574static void
575XkbRF_ApplyRule(	XkbRF_RulePtr 		rule,
576			XkbComponentNamesPtr	names)
577{
578    rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
579
580    Apply(rule->keycodes, &names->keycodes);
581    Apply(rule->symbols,  &names->symbols);
582    Apply(rule->types,    &names->types);
583    Apply(rule->compat,   &names->compat);
584    Apply(rule->geometry, &names->geometry);
585}
586
587static Bool
588CheckGroup(	XkbRF_RulesPtr          rules,
589		char * 			group_name,
590		char * 			name)
591{
592   int i;
593   char *p;
594   XkbRF_GroupPtr group;
595
596   for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
597       if (! strcmp(group->name, group_name)) {
598           break;
599       }
600   }
601   if (i == rules->num_groups)
602       return FALSE;
603   for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
604       if (! strcmp(p, name)) {
605           return TRUE;
606       }
607   }
608   return FALSE;
609}
610
611static int
612XkbRF_CheckApplyRule(	XkbRF_RulePtr 		rule,
613			XkbRF_MultiDefsPtr	mdefs,
614			XkbComponentNamesPtr	names,
615			XkbRF_RulesPtr          rules)
616{
617    Bool pending = FALSE;
618
619    if (rule->model != NULL) {
620        if(mdefs->model == NULL)
621            return 0;
622        if (strcmp(rule->model, "*") == 0) {
623            pending = TRUE;
624        } else {
625            if (rule->model[0] == '$') {
626               if (!CheckGroup(rules, rule->model, mdefs->model))
627                  return 0;
628            } else {
629	       if (strcmp(rule->model, mdefs->model) != 0)
630	          return 0;
631	    }
632	}
633    }
634    if (rule->option != NULL) {
635	if (mdefs->options == NULL)
636	    return 0;
637	if ((!MatchOneOf(rule->option,mdefs->options)))
638	    return 0;
639    }
640
641    if (rule->layout != NULL) {
642	if(mdefs->layout[rule->layout_num] == NULL ||
643	   *mdefs->layout[rule->layout_num] == '\0')
644	    return 0;
645        if (strcmp(rule->layout, "*") == 0) {
646            pending = TRUE;
647        } else {
648            if (rule->layout[0] == '$') {
649               if (!CheckGroup(rules, rule->layout,
650                               mdefs->layout[rule->layout_num]))
651                  return 0;
652	    } else {
653	       if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
654	           return 0;
655	    }
656	}
657    }
658    if (rule->variant != NULL) {
659	if (mdefs->variant[rule->variant_num] == NULL ||
660	    *mdefs->variant[rule->variant_num] == '\0')
661	    return 0;
662        if (strcmp(rule->variant, "*") == 0) {
663            pending = TRUE;
664        } else {
665            if (rule->variant[0] == '$') {
666               if (!CheckGroup(rules, rule->variant,
667                               mdefs->variant[rule->variant_num]))
668                  return 0;
669            } else {
670	       if (strcmp(rule->variant,
671                          mdefs->variant[rule->variant_num]) != 0)
672	           return 0;
673	    }
674	}
675    }
676    if (pending) {
677        rule->flags|= XkbRF_PendingMatch;
678	return rule->number;
679    }
680    /* exact match, apply it now */
681    XkbRF_ApplyRule(rule,names);
682    return rule->number;
683}
684
685static void
686XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
687{
688register int 	i;
689XkbRF_RulePtr	rule;
690
691    for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
692	rule->flags&= ~XkbRF_PendingMatch;
693    }
694}
695
696static void
697XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules,XkbComponentNamesPtr names)
698{
699int		i;
700XkbRF_RulePtr	rule;
701
702    for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
703	if ((rule->flags&XkbRF_PendingMatch)==0)
704	    continue;
705	XkbRF_ApplyRule(rule,names);
706    }
707}
708
709static void
710XkbRF_CheckApplyRules(	XkbRF_RulesPtr 		rules,
711			XkbRF_MultiDefsPtr	mdefs,
712			XkbComponentNamesPtr	names,
713			int			flags)
714{
715int		i;
716XkbRF_RulePtr	rule;
717int		skip;
718
719    for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
720	if ((rule->flags & flags) != flags)
721	    continue;
722	skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
723	if (skip && !(flags & XkbRF_Option)) {
724	    for ( ;(i < rules->num_rules) && (rule->number == skip);
725		  rule++, i++);
726	    rule--; i--;
727	}
728    }
729}
730
731/***====================================================================***/
732
733static char *
734XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
735{
736char 	*str, *outstr, *orig, *var;
737int	len, ndx;
738
739    orig= name;
740    str= index(name,'%');
741    if (str==NULL)
742	return name;
743    len= strlen(name);
744    while (str!=NULL) {
745	char pfx= str[1];
746	int   extra_len= 0;
747	if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
748	    extra_len= 1;
749	    str++;
750	}
751	else if (pfx=='(') {
752	    extra_len= 2;
753	    str++;
754	}
755	var = str + 1;
756	str = get_index(var + 1, &ndx);
757	if (ndx == -1) {
758	    str = index(str,'%');
759	    continue;
760        }
761	if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
762	    len+= strlen(mdefs->layout[ndx])+extra_len;
763	else if ((*var=='m')&&mdefs->model)
764	    len+= strlen(mdefs->model)+extra_len;
765	else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
766	    len+= strlen(mdefs->variant[ndx])+extra_len;
767	if ((pfx=='(')&&(*str==')')) {
768	    str++;
769	}
770	str= index(&str[0],'%');
771    }
772    name= malloc(len+1);
773    str= orig;
774    outstr= name;
775    while (*str!='\0') {
776	if (str[0]=='%') {
777	    char pfx,sfx;
778	    str++;
779	    pfx= str[0];
780	    sfx= '\0';
781	    if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
782		str++;
783	    }
784	    else if (pfx=='(') {
785		sfx= ')';
786		str++;
787	    }
788	    else pfx= '\0';
789
790	    var = str;
791	    str = get_index(var + 1, &ndx);
792	    if (ndx == -1) {
793	        continue;
794            }
795	    if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
796		if (pfx) *outstr++= pfx;
797		strcpy(outstr,mdefs->layout[ndx]);
798		outstr+= strlen(mdefs->layout[ndx]);
799		if (sfx) *outstr++= sfx;
800	    }
801	    else if ((*var=='m')&&(mdefs->model)) {
802		if (pfx) *outstr++= pfx;
803		strcpy(outstr,mdefs->model);
804		outstr+= strlen(mdefs->model);
805		if (sfx) *outstr++= sfx;
806	    }
807	    else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
808		if (pfx) *outstr++= pfx;
809		strcpy(outstr,mdefs->variant[ndx]);
810		outstr+= strlen(mdefs->variant[ndx]);
811		if (sfx) *outstr++= sfx;
812	    }
813	    if ((pfx=='(')&&(*str==')'))
814		str++;
815	}
816	else {
817	    *outstr++= *str++;
818	}
819    }
820    *outstr++= '\0';
821    if (orig!=name)
822	free(orig);
823    return name;
824}
825
826/***====================================================================***/
827
828Bool
829XkbRF_GetComponents(	XkbRF_RulesPtr		rules,
830			XkbRF_VarDefsPtr	defs,
831			XkbComponentNamesPtr	names)
832{
833    XkbRF_MultiDefsRec mdefs;
834
835    MakeMultiDefs(&mdefs, defs);
836
837    memset((char *)names, 0, sizeof(XkbComponentNamesRec));
838    XkbRF_ClearPartialMatches(rules);
839    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
840    XkbRF_ApplyPartialMatches(rules, names);
841    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
842    XkbRF_ApplyPartialMatches(rules, names);
843    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
844
845    if (names->keycodes)
846	names->keycodes= XkbRF_SubstituteVars(names->keycodes, &mdefs);
847    if (names->symbols)
848	names->symbols=	XkbRF_SubstituteVars(names->symbols, &mdefs);
849    if (names->types)
850	names->types= XkbRF_SubstituteVars(names->types, &mdefs);
851    if (names->compat)
852	names->compat= XkbRF_SubstituteVars(names->compat, &mdefs);
853    if (names->geometry)
854	names->geometry= XkbRF_SubstituteVars(names->geometry, &mdefs);
855
856    FreeMultiDefs(&mdefs);
857    return (names->keycodes && names->symbols && names->types &&
858		names->compat && names->geometry);
859}
860
861static XkbRF_RulePtr
862XkbRF_AddRule(XkbRF_RulesPtr	rules)
863{
864    if (rules->sz_rules<1) {
865	rules->sz_rules= 16;
866	rules->num_rules= 0;
867	rules->rules= calloc(rules->sz_rules, sizeof(XkbRF_RuleRec));
868    }
869    else if (rules->num_rules>=rules->sz_rules) {
870	rules->sz_rules*= 2;
871	rules->rules= realloc(rules->rules,
872				rules->sz_rules * sizeof(XkbRF_RuleRec));
873    }
874    if (!rules->rules) {
875	rules->sz_rules= rules->num_rules= 0;
876	DebugF("Allocation failure in XkbRF_AddRule\n");
877	return NULL;
878    }
879    memset((char *)&rules->rules[rules->num_rules], 0, sizeof(XkbRF_RuleRec));
880    return &rules->rules[rules->num_rules++];
881}
882
883static XkbRF_GroupPtr
884XkbRF_AddGroup(XkbRF_RulesPtr	rules)
885{
886    if (rules->sz_groups<1) {
887	rules->sz_groups= 16;
888	rules->num_groups= 0;
889	rules->groups= calloc(rules->sz_groups, sizeof(XkbRF_GroupRec));
890    }
891    else if (rules->num_groups >= rules->sz_groups) {
892	rules->sz_groups *= 2;
893	rules->groups= realloc(rules->groups,
894				rules->sz_groups * sizeof(XkbRF_GroupRec));
895    }
896    if (!rules->groups) {
897	rules->sz_groups= rules->num_groups= 0;
898	return NULL;
899    }
900
901    memset((char *)&rules->groups[rules->num_groups], 0, sizeof(XkbRF_GroupRec));
902    return &rules->groups[rules->num_groups++];
903}
904
905Bool
906XkbRF_LoadRules(FILE *file, XkbRF_RulesPtr rules)
907{
908InputLine	line;
909RemapSpec	remap;
910XkbRF_RuleRec	trule,*rule;
911XkbRF_GroupRec  tgroup,*group;
912
913    if (!(rules && file))
914	return FALSE;
915    memset((char *)&remap, 0, sizeof(RemapSpec));
916    memset((char *)&tgroup, 0, sizeof(XkbRF_GroupRec));
917    InitInputLine(&line);
918    while (GetInputLine(file,&line,TRUE)) {
919	if (CheckLine(&line,&remap,&trule,&tgroup)) {
920            if (tgroup.number) {
921	        if ((group= XkbRF_AddGroup(rules))!=NULL) {
922		    *group= tgroup;
923		    memset((char *)&tgroup, 0, sizeof(XkbRF_GroupRec));
924	        }
925	    } else {
926	        if ((rule= XkbRF_AddRule(rules))!=NULL) {
927		    *rule= trule;
928		    memset((char *)&trule, 0, sizeof(XkbRF_RuleRec));
929	        }
930	    }
931	}
932	line.num_line= 0;
933    }
934    FreeInputLine(&line);
935    return TRUE;
936}
937
938Bool
939XkbRF_LoadRulesByName(char *base,char *locale,XkbRF_RulesPtr rules)
940{
941FILE *		file;
942char		buf[PATH_MAX];
943Bool		ok;
944
945    if ((!base)||(!rules))
946	return FALSE;
947    if (locale) {
948	if (strlen(base)+strlen(locale)+2 > PATH_MAX)
949	    return FALSE;
950	sprintf(buf,"%s-%s", base, locale);
951    }
952    else {
953	if (strlen(base)+1 > PATH_MAX)
954	    return FALSE;
955	strcpy(buf,base);
956    }
957
958    file= fopen(buf, "r");
959    if ((!file)&&(locale)) { /* fallback if locale was specified */
960	strcpy(buf,base);
961	file= fopen(buf, "r");
962    }
963    if (!file)
964	return FALSE;
965    ok= XkbRF_LoadRules(file,rules);
966    fclose(file);
967    return ok;
968}
969
970/***====================================================================***/
971
972XkbRF_RulesPtr
973XkbRF_Create(void)
974{
975    return calloc(1, sizeof( XkbRF_RulesRec));
976}
977
978/***====================================================================***/
979
980void
981XkbRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
982{
983int		i;
984XkbRF_RulePtr	rule;
985XkbRF_GroupPtr	group;
986
987    if (!rules)
988	return;
989    if (rules->rules) {
990	for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
991	    free(rule->model);
992	    free(rule->layout);
993	    free(rule->variant);
994	    free(rule->option);
995	    free(rule->keycodes);
996	    free(rule->symbols);
997	    free(rule->types);
998	    free(rule->compat);
999	    free(rule->geometry);
1000	    memset((char *)rule, 0, sizeof(XkbRF_RuleRec));
1001	}
1002	free(rules->rules);
1003	rules->num_rules= rules->sz_rules= 0;
1004	rules->rules= NULL;
1005    }
1006
1007    if (rules->groups) {
1008	for (i=0, group=rules->groups;i<rules->num_groups;i++,group++) {
1009	    free(group->name);
1010	    free(group->words);
1011	}
1012	free(rules->groups);
1013	rules->num_groups= 0;
1014	rules->groups= NULL;
1015    }
1016    if (freeRules)
1017	free(rules);
1018    return;
1019}
1020