maprules.c revision 4642e01f
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#define NEED_EVENTS
40#include <X11/Xproto.h>
41#include <X11/X.h>
42#include <X11/Xos.h>
43#include <X11/Xfuncs.h>
44#include <X11/Xatom.h>
45#include <X11/keysym.h>
46#include "misc.h"
47#include "inputstr.h"
48#include "dix.h"
49#include "os.h"
50#include "xkbstr.h"
51#define XKBSRV_NEED_FILE_FUNCS
52#include <xkbsrv.h>
53
54/***====================================================================***/
55
56#define DFLT_LINE_SIZE	128
57
58typedef struct {
59	int	line_num;
60	int	sz_line;
61	int	num_line;
62	char	buf[DFLT_LINE_SIZE];
63	char *	line;
64} InputLine;
65
66static void
67InitInputLine(InputLine *line)
68{
69    line->line_num= 1;
70    line->num_line= 0;
71    line->sz_line= DFLT_LINE_SIZE;
72    line->line=	line->buf;
73    return;
74}
75
76static void
77FreeInputLine(InputLine *line)
78{
79    if (line->line!=line->buf)
80	_XkbFree(line->line);
81    line->line_num= 1;
82    line->num_line= 0;
83    line->sz_line= DFLT_LINE_SIZE;
84    line->line= line->buf;
85    return;
86}
87
88static int
89InputLineAddChar(InputLine *line,int ch)
90{
91    if (line->num_line>=line->sz_line) {
92	if (line->line==line->buf) {
93	    line->line= (char *)_XkbAlloc(line->sz_line*2);
94	    memcpy(line->line,line->buf,line->sz_line);
95	}
96	else {
97	    line->line=(char *)_XkbRealloc((char *)line->line,line->sz_line*2);
98	}
99	line->sz_line*= 2;
100    }
101    line->line[line->num_line++]= ch;
102    return ch;
103}
104
105#define	ADD_CHAR(l,c)	((l)->num_line<(l)->sz_line?\
106				(int)((l)->line[(l)->num_line++]= (c)):\
107				InputLineAddChar(l,c))
108
109static Bool
110GetInputLine(FILE *file,InputLine *line,Bool checkbang)
111{
112int	ch;
113Bool	endOfFile,spacePending,slashPending,inComment;
114
115     endOfFile= False;
116     while ((!endOfFile)&&(line->num_line==0)) {
117	spacePending= slashPending= inComment= False;
118	while (((ch=getc(file))!='\n')&&(ch!=EOF)) {
119	    if (ch=='\\') {
120		if ((ch=getc(file))==EOF)
121		    break;
122		if (ch=='\n') {
123		    inComment= False;
124		    ch= ' ';
125		    line->line_num++;
126		}
127	    }
128	    if (inComment)
129		continue;
130	    if (ch=='/') {
131		if (slashPending) {
132		    inComment= True;
133		    slashPending= False;
134		}
135		else {
136		    slashPending= True;
137		}
138		continue;
139	    }
140	    else if (slashPending) {
141		if (spacePending) {
142		    ADD_CHAR(line,' ');
143		    spacePending= False;
144		}
145		ADD_CHAR(line,'/');
146		slashPending= False;
147	    }
148	    if (isspace(ch)) {
149		while (isspace(ch)&&(ch!='\n')&&(ch!=EOF)) {
150		    ch= getc(file);
151		}
152		if (ch==EOF)
153		    break;
154		if ((ch!='\n')&&(line->num_line>0))
155		    spacePending= True;
156		ungetc(ch,file);
157	    }
158	    else {
159		if (spacePending) {
160		    ADD_CHAR(line,' ');
161		    spacePending= False;
162		}
163		if (checkbang && ch=='!') {
164		    if (line->num_line!=0) {
165			DebugF("The '!' legal only at start of line\n");
166			DebugF("Line containing '!' ignored\n");
167			line->num_line= 0;
168			inComment= 0;
169			break;
170		    }
171
172		}
173		ADD_CHAR(line,ch);
174	    }
175	}
176	if (ch==EOF)
177	     endOfFile= True;
178/*	else line->num_line++;*/
179     }
180     if ((line->num_line==0)&&(endOfFile))
181	return False;
182      ADD_CHAR(line,'\0');
183      return True;
184}
185
186/***====================================================================***/
187
188#define	MODEL		0
189#define	LAYOUT		1
190#define	VARIANT		2
191#define	OPTION		3
192#define	KEYCODES	4
193#define SYMBOLS		5
194#define	TYPES		6
195#define	COMPAT		7
196#define	GEOMETRY	8
197#define	KEYMAP		9
198#define	MAX_WORDS	10
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", "keymap"
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   bzero((char *)remap,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   if (((present&COMPONENT_MASK)&(1<<KEYMAP))&&
341				((present&COMPONENT_MASK)!=(1<<KEYMAP))) {
342	DebugF("Keymap cannot appear with other components\n");
343	DebugF("Illegal mapping ignored\n");
344	remap->num_remap= 0;
345	return;
346   }
347   remap->number++;
348   return;
349}
350
351static Bool
352MatchOneOf(char *wanted,char *vals_defined)
353{
354char	*str,*next;
355int	want_len= strlen(wanted);
356
357    for (str=vals_defined,next=NULL;str!=NULL;str=next) {
358	int len;
359	next= strchr(str,',');
360	if (next) {
361	    len= next-str;
362	    next++;
363	}
364	else {
365	    len= strlen(str);
366	}
367	if ((len==want_len)&&(strncmp(wanted,str,len)==0))
368	    return True;
369    }
370    return False;
371}
372
373/***====================================================================***/
374
375static Bool
376CheckLine(	InputLine *		line,
377		RemapSpec *		remap,
378		XkbRF_RulePtr		rule,
379		XkbRF_GroupPtr		group)
380{
381char *		str,*tok;
382register int	nread, i;
383FileSpec	tmp;
384_Xstrtokparams	strtok_buf;
385Bool 		append = False;
386
387    if (line->line[0]=='!') {
388        if (line->line[1] == '$' ||
389            (line->line[1] == ' ' && line->line[2] == '$')) {
390            char *gname = strchr(line->line, '$');
391            char *words = strchr(gname, ' ');
392            if(!words)
393                return False;
394            *words++ = '\0';
395            for (; *words; words++) {
396                if (*words != '=' && *words != ' ')
397                    break;
398            }
399            if (*words == '\0')
400                return False;
401            group->name = _XkbDupString(gname);
402            group->words = _XkbDupString(words);
403            for (i = 1, words = group->words; *words; words++) {
404                 if ( *words == ' ') {
405                     *words++ = '\0';
406                     i++;
407                 }
408            }
409            group->number = i;
410            return True;
411        } else {
412	    SetUpRemap(line,remap);
413	    return False;
414        }
415    }
416
417    if (remap->num_remap==0) {
418	DebugF("Must have a mapping before first line of data\n");
419	DebugF("Illegal line of data ignored\n");
420	return False;
421    }
422    bzero((char *)&tmp,sizeof(FileSpec));
423    str= line->line;
424    for (nread= 0;(tok=_XStrtok(str," ",strtok_buf))!=NULL;nread++) {
425	str= NULL;
426	if (strcmp(tok,"=")==0) {
427	    nread--;
428	    continue;
429	}
430	if (nread>remap->num_remap) {
431	    DebugF("Too many words on a line\n");
432	    DebugF("Extra word \"%s\" ignored\n",tok);
433	    continue;
434	}
435	tmp.name[remap->remap[nread].word]= tok;
436	if (*tok == '+' || *tok == '|')
437	    append = True;
438    }
439    if (nread<remap->num_remap) {
440	DebugF("Too few words on a line: %s\n", line->line);
441	DebugF("line ignored\n");
442	return False;
443    }
444
445    rule->flags= 0;
446    rule->number = remap->number;
447    if (tmp.name[OPTION])
448	 rule->flags|= XkbRF_Option;
449    else if (append)
450	 rule->flags|= XkbRF_Append;
451    else
452	 rule->flags|= XkbRF_Normal;
453    rule->model= _XkbDupString(tmp.name[MODEL]);
454    rule->layout= _XkbDupString(tmp.name[LAYOUT]);
455    rule->variant= _XkbDupString(tmp.name[VARIANT]);
456    rule->option= _XkbDupString(tmp.name[OPTION]);
457
458    rule->keycodes= _XkbDupString(tmp.name[KEYCODES]);
459    rule->symbols= _XkbDupString(tmp.name[SYMBOLS]);
460    rule->types= _XkbDupString(tmp.name[TYPES]);
461    rule->compat= _XkbDupString(tmp.name[COMPAT]);
462    rule->geometry= _XkbDupString(tmp.name[GEOMETRY]);
463    rule->keymap= NULL;
464
465    rule->layout_num = rule->variant_num = 0;
466    for (i = 0; i < nread; i++) {
467        if (remap->remap[i].index) {
468	    if (remap->remap[i].word == LAYOUT)
469	        rule->layout_num = remap->remap[i].index;
470	    if (remap->remap[i].word == VARIANT)
471	        rule->variant_num = remap->remap[i].index;
472        }
473    }
474    return True;
475}
476
477static char *
478_Concat(char *str1,char *str2)
479{
480int len;
481
482    if ((!str1)||(!str2))
483	return str1;
484    len= strlen(str1)+strlen(str2)+1;
485    str1= _XkbTypedRealloc(str1,len,char);
486    if (str1)
487	strcat(str1,str2);
488    return str1;
489}
490
491static void
492squeeze_spaces(char *p1)
493{
494   char *p2;
495   for (p2 = p1; *p2; p2++) {
496       *p1 = *p2;
497       if (*p1 != ' ') p1++;
498   }
499   *p1 = '\0';
500}
501
502static Bool
503MakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs)
504{
505
506   bzero((char *)mdefs,sizeof(XkbRF_MultiDefsRec));
507   mdefs->model = defs->model;
508   mdefs->options = _XkbDupString(defs->options);
509   if (mdefs->options) squeeze_spaces(mdefs->options);
510
511   if (defs->layout) {
512       if (!strchr(defs->layout, ',')) {
513           mdefs->layout[0] = defs->layout;
514       } else {
515           char *p;
516           int i;
517           mdefs->layout[1] = _XkbDupString(defs->layout);
518	   if (mdefs->layout[1] == NULL)
519	      return False;
520           squeeze_spaces(mdefs->layout[1]);
521           p = mdefs->layout[1];
522           for (i = 2; i <= XkbNumKbdGroups; i++) {
523              if ((p = strchr(p, ','))) {
524                 *p++ = '\0';
525                 mdefs->layout[i] = p;
526              } else {
527                 break;
528              }
529           }
530           if (p && (p = strchr(p, ',')))
531              *p = '\0';
532       }
533   }
534
535   if (defs->variant) {
536       if (!strchr(defs->variant, ',')) {
537           mdefs->variant[0] = defs->variant;
538       } else {
539           char *p;
540           int i;
541           mdefs->variant[1] = _XkbDupString(defs->variant);
542	   if (mdefs->variant[1] == NULL)
543	      return False;
544           squeeze_spaces(mdefs->variant[1]);
545           p = mdefs->variant[1];
546           for (i = 2; i <= XkbNumKbdGroups; i++) {
547              if ((p = strchr(p, ','))) {
548                 *p++ = '\0';
549                 mdefs->variant[i] = p;
550              } else {
551                 break;
552              }
553           }
554           if (p && (p = strchr(p, ',')))
555              *p = '\0';
556       }
557   }
558   return True;
559}
560
561static void
562FreeMultiDefs(XkbRF_MultiDefsPtr defs)
563{
564  if (defs->options) _XkbFree(defs->options);
565  if (defs->layout[1])  _XkbFree(defs->layout[1]);
566  if (defs->variant[1])  _XkbFree(defs->variant[1]);
567}
568
569static void
570Apply(char *src, char **dst)
571{
572    if (src) {
573        if (*src == '+' || *src == '!') {
574	    *dst= _Concat(*dst, src);
575        } else {
576            if (*dst == NULL)
577	        *dst= _XkbDupString(src);
578        }
579    }
580}
581
582static void
583XkbRF_ApplyRule(	XkbRF_RulePtr 		rule,
584			XkbComponentNamesPtr	names)
585{
586    rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
587
588    Apply(rule->keycodes, &names->keycodes);
589    Apply(rule->symbols,  &names->symbols);
590    Apply(rule->types,    &names->types);
591    Apply(rule->compat,   &names->compat);
592    Apply(rule->geometry, &names->geometry);
593}
594
595static Bool
596CheckGroup(	XkbRF_RulesPtr          rules,
597		char * 			group_name,
598		char * 			name)
599{
600   int i;
601   char *p;
602   XkbRF_GroupPtr group;
603
604   for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
605       if (! strcmp(group->name, group_name)) {
606           break;
607       }
608   }
609   if (i == rules->num_groups)
610       return False;
611   for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
612       if (! strcmp(p, name)) {
613           return True;
614       }
615   }
616   return False;
617}
618
619static int
620XkbRF_CheckApplyRule(	XkbRF_RulePtr 		rule,
621			XkbRF_MultiDefsPtr	mdefs,
622			XkbComponentNamesPtr	names,
623			XkbRF_RulesPtr          rules)
624{
625    Bool pending = False;
626
627    if (rule->model != NULL) {
628        if(mdefs->model == NULL)
629            return 0;
630        if (strcmp(rule->model, "*") == 0) {
631            pending = True;
632        } else {
633            if (rule->model[0] == '$') {
634               if (!CheckGroup(rules, rule->model, mdefs->model))
635                  return 0;
636            } else {
637	       if (strcmp(rule->model, mdefs->model) != 0)
638	          return 0;
639	    }
640	}
641    }
642    if (rule->option != NULL) {
643	if (mdefs->options == NULL)
644	    return 0;
645	if ((!MatchOneOf(rule->option,mdefs->options)))
646	    return 0;
647    }
648
649    if (rule->layout != NULL) {
650	if(mdefs->layout[rule->layout_num] == NULL ||
651	   *mdefs->layout[rule->layout_num] == '\0')
652	    return 0;
653        if (strcmp(rule->layout, "*") == 0) {
654            pending = True;
655        } else {
656            if (rule->layout[0] == '$') {
657               if (!CheckGroup(rules, rule->layout,
658                               mdefs->layout[rule->layout_num]))
659                  return 0;
660	    } else {
661	       if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
662	           return 0;
663	    }
664	}
665    }
666    if (rule->variant != NULL) {
667	if (mdefs->variant[rule->variant_num] == NULL ||
668	    *mdefs->variant[rule->variant_num] == '\0')
669	    return 0;
670        if (strcmp(rule->variant, "*") == 0) {
671            pending = True;
672        } else {
673            if (rule->variant[0] == '$') {
674               if (!CheckGroup(rules, rule->variant,
675                               mdefs->variant[rule->variant_num]))
676                  return 0;
677            } else {
678	       if (strcmp(rule->variant,
679                          mdefs->variant[rule->variant_num]) != 0)
680	           return 0;
681	    }
682	}
683    }
684    if (pending) {
685        rule->flags|= XkbRF_PendingMatch;
686	return rule->number;
687    }
688    /* exact match, apply it now */
689    XkbRF_ApplyRule(rule,names);
690    return rule->number;
691}
692
693static void
694XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
695{
696register int 	i;
697XkbRF_RulePtr	rule;
698
699    for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
700	rule->flags&= ~XkbRF_PendingMatch;
701    }
702}
703
704static void
705XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules,XkbComponentNamesPtr names)
706{
707int		i;
708XkbRF_RulePtr	rule;
709
710    for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
711	if ((rule->flags&XkbRF_PendingMatch)==0)
712	    continue;
713	XkbRF_ApplyRule(rule,names);
714    }
715}
716
717static void
718XkbRF_CheckApplyRules(	XkbRF_RulesPtr 		rules,
719			XkbRF_MultiDefsPtr	mdefs,
720			XkbComponentNamesPtr	names,
721			int			flags)
722{
723int		i;
724XkbRF_RulePtr	rule;
725int		skip;
726
727    for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
728	if ((rule->flags & flags) != flags)
729	    continue;
730	skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
731	if (skip && !(flags & XkbRF_Option)) {
732	    for ( ;(i < rules->num_rules) && (rule->number == skip);
733		  rule++, i++);
734	    rule--; i--;
735	}
736    }
737}
738
739/***====================================================================***/
740
741static char *
742XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
743{
744char 	*str, *outstr, *orig, *var;
745int	len, ndx;
746
747    orig= name;
748    str= index(name,'%');
749    if (str==NULL)
750	return name;
751    len= strlen(name);
752    while (str!=NULL) {
753	char pfx= str[1];
754	int   extra_len= 0;
755	if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
756	    extra_len= 1;
757	    str++;
758	}
759	else if (pfx=='(') {
760	    extra_len= 2;
761	    str++;
762	}
763	var = str + 1;
764	str = get_index(var + 1, &ndx);
765	if (ndx == -1) {
766	    str = index(str,'%');
767	    continue;
768        }
769	if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
770	    len+= strlen(mdefs->layout[ndx])+extra_len;
771	else if ((*var=='m')&&mdefs->model)
772	    len+= strlen(mdefs->model)+extra_len;
773	else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
774	    len+= strlen(mdefs->variant[ndx])+extra_len;
775	if ((pfx=='(')&&(*str==')')) {
776	    str++;
777	}
778	str= index(&str[0],'%');
779    }
780    name= (char *)_XkbAlloc(len+1);
781    str= orig;
782    outstr= name;
783    while (*str!='\0') {
784	if (str[0]=='%') {
785	    char pfx,sfx;
786	    str++;
787	    pfx= str[0];
788	    sfx= '\0';
789	    if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
790		str++;
791	    }
792	    else if (pfx=='(') {
793		sfx= ')';
794		str++;
795	    }
796	    else pfx= '\0';
797
798	    var = str;
799	    str = get_index(var + 1, &ndx);
800	    if (ndx == -1) {
801	        continue;
802            }
803	    if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
804		if (pfx) *outstr++= pfx;
805		strcpy(outstr,mdefs->layout[ndx]);
806		outstr+= strlen(mdefs->layout[ndx]);
807		if (sfx) *outstr++= sfx;
808	    }
809	    else if ((*var=='m')&&(mdefs->model)) {
810		if (pfx) *outstr++= pfx;
811		strcpy(outstr,mdefs->model);
812		outstr+= strlen(mdefs->model);
813		if (sfx) *outstr++= sfx;
814	    }
815	    else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
816		if (pfx) *outstr++= pfx;
817		strcpy(outstr,mdefs->variant[ndx]);
818		outstr+= strlen(mdefs->variant[ndx]);
819		if (sfx) *outstr++= sfx;
820	    }
821	    if ((pfx=='(')&&(*str==')'))
822		str++;
823	}
824	else {
825	    *outstr++= *str++;
826	}
827    }
828    *outstr++= '\0';
829    if (orig!=name)
830	_XkbFree(orig);
831    return name;
832}
833
834/***====================================================================***/
835
836Bool
837XkbRF_GetComponents(	XkbRF_RulesPtr		rules,
838			XkbRF_VarDefsPtr	defs,
839			XkbComponentNamesPtr	names)
840{
841    XkbRF_MultiDefsRec mdefs;
842
843    MakeMultiDefs(&mdefs, defs);
844
845    bzero((char *)names,sizeof(XkbComponentNamesRec));
846    XkbRF_ClearPartialMatches(rules);
847    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
848    XkbRF_ApplyPartialMatches(rules, names);
849    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
850    XkbRF_ApplyPartialMatches(rules, names);
851    XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
852
853    if (names->keycodes)
854	names->keycodes= XkbRF_SubstituteVars(names->keycodes, &mdefs);
855    if (names->symbols)
856	names->symbols=	XkbRF_SubstituteVars(names->symbols, &mdefs);
857    if (names->types)
858	names->types= XkbRF_SubstituteVars(names->types, &mdefs);
859    if (names->compat)
860	names->compat= XkbRF_SubstituteVars(names->compat, &mdefs);
861    if (names->geometry)
862	names->geometry= XkbRF_SubstituteVars(names->geometry, &mdefs);
863    if (names->keymap)
864	names->keymap= XkbRF_SubstituteVars(names->keymap, &mdefs);
865
866    FreeMultiDefs(&mdefs);
867    return (names->keycodes && names->symbols && names->types &&
868		names->compat && names->geometry ) || names->keymap;
869}
870
871XkbRF_RulePtr
872XkbRF_AddRule(XkbRF_RulesPtr	rules)
873{
874    if (rules->sz_rules<1) {
875	rules->sz_rules= 16;
876	rules->num_rules= 0;
877	rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
878    }
879    else if (rules->num_rules>=rules->sz_rules) {
880	rules->sz_rules*= 2;
881	rules->rules= _XkbTypedRealloc(rules->rules,rules->sz_rules,
882							XkbRF_RuleRec);
883    }
884    if (!rules->rules) {
885	rules->sz_rules= rules->num_rules= 0;
886	DebugF("Allocation failure in XkbRF_AddRule\n");
887	return NULL;
888    }
889    bzero((char *)&rules->rules[rules->num_rules],sizeof(XkbRF_RuleRec));
890    return &rules->rules[rules->num_rules++];
891}
892
893XkbRF_GroupPtr
894XkbRF_AddGroup(XkbRF_RulesPtr	rules)
895{
896    if (rules->sz_groups<1) {
897	rules->sz_groups= 16;
898	rules->num_groups= 0;
899	rules->groups= _XkbTypedCalloc(rules->sz_groups,XkbRF_GroupRec);
900    }
901    else if (rules->num_groups >= rules->sz_groups) {
902	rules->sz_groups *= 2;
903	rules->groups= _XkbTypedRealloc(rules->groups,rules->sz_groups,
904							XkbRF_GroupRec);
905    }
906    if (!rules->groups) {
907	rules->sz_groups= rules->num_groups= 0;
908	return NULL;
909    }
910
911    bzero((char *)&rules->groups[rules->num_groups],sizeof(XkbRF_GroupRec));
912    return &rules->groups[rules->num_groups++];
913}
914
915Bool
916XkbRF_LoadRules(FILE *file, XkbRF_RulesPtr rules)
917{
918InputLine	line;
919RemapSpec	remap;
920XkbRF_RuleRec	trule,*rule;
921XkbRF_GroupRec  tgroup,*group;
922
923    if (!(rules && file))
924	return False;
925    bzero((char *)&remap,sizeof(RemapSpec));
926    bzero((char *)&tgroup,sizeof(XkbRF_GroupRec));
927    InitInputLine(&line);
928    while (GetInputLine(file,&line,True)) {
929	if (CheckLine(&line,&remap,&trule,&tgroup)) {
930            if (tgroup.number) {
931	        if ((group= XkbRF_AddGroup(rules))!=NULL) {
932		    *group= tgroup;
933		    bzero((char *)&tgroup,sizeof(XkbRF_GroupRec));
934	        }
935	    } else {
936	        if ((rule= XkbRF_AddRule(rules))!=NULL) {
937		    *rule= trule;
938		    bzero((char *)&trule,sizeof(XkbRF_RuleRec));
939	        }
940	    }
941	}
942	line.num_line= 0;
943    }
944    FreeInputLine(&line);
945    return True;
946}
947
948Bool
949XkbRF_LoadRulesByName(char *base,char *locale,XkbRF_RulesPtr rules)
950{
951FILE *		file;
952char		buf[PATH_MAX];
953Bool		ok;
954
955    if ((!base)||(!rules))
956	return False;
957    if (locale) {
958	if (strlen(base)+strlen(locale)+2 > PATH_MAX)
959	    return False;
960	sprintf(buf,"%s-%s", base, locale);
961    }
962    else {
963	if (strlen(base)+1 > PATH_MAX)
964	    return False;
965	strcpy(buf,base);
966    }
967
968    file= fopen(buf, "r");
969    if ((!file)&&(locale)) { /* fallback if locale was specified */
970	strcpy(buf,base);
971	file= fopen(buf, "r");
972    }
973    if (!file)
974	return False;
975    ok= XkbRF_LoadRules(file,rules);
976    fclose(file);
977    return ok;
978}
979
980/***====================================================================***/
981
982#define HEAD_NONE	0
983#define HEAD_MODEL	1
984#define HEAD_LAYOUT	2
985#define HEAD_VARIANT	3
986#define HEAD_OPTION	4
987#define	HEAD_EXTRA	5
988
989XkbRF_VarDescPtr
990XkbRF_AddVarDesc(XkbRF_DescribeVarsPtr	vars)
991{
992    if (vars->sz_desc<1) {
993	vars->sz_desc= 16;
994	vars->num_desc= 0;
995	vars->desc= _XkbTypedCalloc(vars->sz_desc,XkbRF_VarDescRec);
996    }
997    else if (vars->num_desc>=vars->sz_desc) {
998	vars->sz_desc*= 2;
999	vars->desc= _XkbTypedRealloc(vars->desc,vars->sz_desc,XkbRF_VarDescRec);
1000    }
1001    if (!vars->desc) {
1002	vars->sz_desc= vars->num_desc= 0;
1003	DebugF("Allocation failure in XkbRF_AddVarDesc\n");
1004	return NULL;
1005    }
1006    vars->desc[vars->num_desc].name= NULL;
1007    vars->desc[vars->num_desc].desc= NULL;
1008    return &vars->desc[vars->num_desc++];
1009}
1010
1011XkbRF_VarDescPtr
1012XkbRF_AddVarDescCopy(XkbRF_DescribeVarsPtr vars,XkbRF_VarDescPtr from)
1013{
1014XkbRF_VarDescPtr	nd;
1015
1016    if ((nd=XkbRF_AddVarDesc(vars))!=NULL) {
1017	nd->name= _XkbDupString(from->name);
1018	nd->desc= _XkbDupString(from->desc);
1019    }
1020    return nd;
1021}
1022
1023XkbRF_DescribeVarsPtr
1024XkbRF_AddVarToDescribe(XkbRF_RulesPtr rules,char *name)
1025{
1026    if (rules->sz_extra<1) {
1027	rules->num_extra= 0;
1028	rules->sz_extra= 1;
1029	rules->extra_names= _XkbTypedCalloc(rules->sz_extra,char *);
1030	rules->extra= _XkbTypedCalloc(rules->sz_extra, XkbRF_DescribeVarsRec);
1031    }
1032    else if (rules->num_extra>=rules->sz_extra) {
1033	rules->sz_extra*= 2;
1034	rules->extra_names= _XkbTypedRealloc(rules->extra_names,rules->sz_extra,
1035								char *);
1036	rules->extra=_XkbTypedRealloc(rules->extra, rules->sz_extra,
1037							XkbRF_DescribeVarsRec);
1038    }
1039    if ((!rules->extra_names)||(!rules->extra)) {
1040	DebugF("allocation error in extra parts\n");
1041	rules->sz_extra= rules->num_extra= 0;
1042	rules->extra_names= NULL;
1043	rules->extra= NULL;
1044	return NULL;
1045    }
1046    rules->extra_names[rules->num_extra]= _XkbDupString(name);
1047    bzero(&rules->extra[rules->num_extra],sizeof(XkbRF_DescribeVarsRec));
1048    return &rules->extra[rules->num_extra++];
1049}
1050
1051Bool
1052XkbRF_LoadDescriptions(FILE *file,XkbRF_RulesPtr rules)
1053{
1054InputLine		line;
1055XkbRF_VarDescRec	tmp;
1056char			*tok;
1057int			len,headingtype,extra_ndx = 0;
1058
1059    bzero((char *)&tmp, sizeof(XkbRF_VarDescRec));
1060    headingtype = HEAD_NONE;
1061    InitInputLine(&line);
1062    for ( ; GetInputLine(file,&line,False); line.num_line= 0) {
1063	if (line.line[0]=='!') {
1064	    tok = strtok(&(line.line[1]), " \t");
1065	    if (strcasecmp(tok,"model") == 0)
1066		headingtype = HEAD_MODEL;
1067	    else if (strcasecmp(tok,"layout") == 0)
1068		headingtype = HEAD_LAYOUT;
1069	    else if (strcasecmp(tok,"variant") == 0)
1070		headingtype = HEAD_VARIANT;
1071	    else if (strcasecmp(tok,"option") == 0)
1072		headingtype = HEAD_OPTION;
1073	    else {
1074		int i;
1075		headingtype = HEAD_EXTRA;
1076		extra_ndx= -1;
1077		for (i=0;(i<rules->num_extra)&&(extra_ndx<0);i++) {
1078		    if (!strcasecmp(tok,rules->extra_names[i]))
1079			extra_ndx= i;
1080		}
1081		if (extra_ndx<0) {
1082		    XkbRF_DescribeVarsPtr	var;
1083		    DebugF("Extra heading \"%s\" encountered\n",tok);
1084		    var= XkbRF_AddVarToDescribe(rules,tok);
1085		    if (var)
1086			 extra_ndx= var-rules->extra;
1087		    else headingtype= HEAD_NONE;
1088		}
1089	    }
1090	    continue;
1091	}
1092
1093	if (headingtype == HEAD_NONE) {
1094	    DebugF("Must have a heading before first line of data\n");
1095	    DebugF("Illegal line of data ignored\n");
1096	    continue;
1097	}
1098
1099	len = strlen(line.line);
1100	if ((tmp.name= strtok(line.line, " \t")) == NULL) {
1101	    DebugF("Huh? No token on line\n");
1102	    DebugF("Illegal line of data ignored\n");
1103	    continue;
1104	}
1105	if (strlen(tmp.name) == len) {
1106	    DebugF("No description found\n");
1107	    DebugF("Illegal line of data ignored\n");
1108	    continue;
1109	}
1110
1111	tok = line.line + strlen(tmp.name) + 1;
1112	while ((*tok!='\n')&&isspace(*tok))
1113		tok++;
1114	if (*tok == '\0') {
1115	    DebugF("No description found\n");
1116	    DebugF("Illegal line of data ignored\n");
1117	    continue;
1118	}
1119	tmp.desc= tok;
1120	switch (headingtype) {
1121	    case HEAD_MODEL:
1122		XkbRF_AddVarDescCopy(&rules->models,&tmp);
1123		break;
1124	    case HEAD_LAYOUT:
1125		XkbRF_AddVarDescCopy(&rules->layouts,&tmp);
1126		break;
1127	    case HEAD_VARIANT:
1128		XkbRF_AddVarDescCopy(&rules->variants,&tmp);
1129		break;
1130	    case HEAD_OPTION:
1131		XkbRF_AddVarDescCopy(&rules->options,&tmp);
1132		break;
1133	    case HEAD_EXTRA:
1134		XkbRF_AddVarDescCopy(&rules->extra[extra_ndx],&tmp);
1135		break;
1136	}
1137    }
1138    FreeInputLine(&line);
1139    if ((rules->models.num_desc==0) && (rules->layouts.num_desc==0) &&
1140	(rules->variants.num_desc==0) && (rules->options.num_desc==0) &&
1141	(rules->num_extra==0)) {
1142	return False;
1143    }
1144    return True;
1145}
1146
1147Bool
1148XkbRF_LoadDescriptionsByName(char *base,char *locale,XkbRF_RulesPtr rules)
1149{
1150FILE *		file;
1151char		buf[PATH_MAX];
1152Bool		ok;
1153
1154    if ((!base)||(!rules))
1155	return False;
1156    if (locale) {
1157	if (strlen(base)+strlen(locale)+6 > PATH_MAX)
1158	    return False;
1159	sprintf(buf,"%s-%s.lst", base, locale);
1160    }
1161    else {
1162	if (strlen(base)+5 > PATH_MAX)
1163	    return False;
1164	sprintf(buf,"%s.lst", base);
1165    }
1166
1167    file= fopen(buf, "r");
1168    if ((!file)&&(locale)) { /* fallback if locale was specified */
1169	sprintf(buf,"%s.lst", base);
1170
1171	file= fopen(buf, "r");
1172    }
1173    if (!file)
1174	return False;
1175    ok= XkbRF_LoadDescriptions(file,rules);
1176    fclose(file);
1177    return ok;
1178}
1179
1180/***====================================================================***/
1181
1182XkbRF_RulesPtr
1183XkbRF_Load(char *base,char *locale,Bool wantDesc,Bool wantRules)
1184{
1185XkbRF_RulesPtr	rules;
1186
1187    if ((!base)||((!wantDesc)&&(!wantRules)))
1188	return NULL;
1189    if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
1190	return NULL;
1191    if (wantDesc&&(!XkbRF_LoadDescriptionsByName(base,locale,rules))) {
1192	XkbRF_Free(rules,True);
1193	return NULL;
1194    }
1195    if (wantRules&&(!XkbRF_LoadRulesByName(base,locale,rules))) {
1196	XkbRF_Free(rules,True);
1197	return NULL;
1198    }
1199    return rules;
1200}
1201
1202XkbRF_RulesPtr
1203XkbRF_Create(int szRules,int szExtra)
1204{
1205XkbRF_RulesPtr rules;
1206
1207    if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
1208	return NULL;
1209    if (szRules>0) {
1210	rules->sz_rules= szRules;
1211	rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
1212	if (!rules->rules) {
1213	    _XkbFree(rules);
1214	    return NULL;
1215	}
1216    }
1217    if (szExtra>0) {
1218	rules->sz_extra= szExtra;
1219	rules->extra= _XkbTypedCalloc(rules->sz_extra,XkbRF_DescribeVarsRec);
1220	if (!rules->extra) {
1221	    if (rules->rules)
1222		_XkbFree(rules->rules);
1223	    _XkbFree(rules);
1224	    return NULL;
1225	}
1226    }
1227    return rules;
1228}
1229
1230/***====================================================================***/
1231
1232static void
1233XkbRF_ClearVarDescriptions(XkbRF_DescribeVarsPtr var)
1234{
1235register int i;
1236
1237    for (i=0;i<var->num_desc;i++) {
1238	if (var->desc[i].name)
1239	    _XkbFree(var->desc[i].name);
1240	if (var->desc[i].desc)
1241	    _XkbFree(var->desc[i].desc);
1242	var->desc[i].name= var->desc[i].desc= NULL;
1243    }
1244    if (var->desc)
1245	_XkbFree(var->desc);
1246    var->desc= NULL;
1247    return;
1248}
1249
1250void
1251XkbRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
1252{
1253int		i;
1254XkbRF_RulePtr	rule;
1255XkbRF_GroupPtr	group;
1256
1257    if (!rules)
1258	return;
1259    XkbRF_ClearVarDescriptions(&rules->models);
1260    XkbRF_ClearVarDescriptions(&rules->layouts);
1261    XkbRF_ClearVarDescriptions(&rules->variants);
1262    XkbRF_ClearVarDescriptions(&rules->options);
1263    if (rules->extra) {
1264	for (i = 0; i < rules->num_extra; i++) {
1265	    XkbRF_ClearVarDescriptions(&rules->extra[i]);
1266	}
1267	_XkbFree(rules->extra);
1268	rules->num_extra= rules->sz_extra= 0;
1269	rules->extra= NULL;
1270    }
1271    if (rules->rules) {
1272	for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
1273	    if (rule->model)	_XkbFree(rule->model);
1274	    if (rule->layout)	_XkbFree(rule->layout);
1275	    if (rule->variant)	_XkbFree(rule->variant);
1276	    if (rule->option)	_XkbFree(rule->option);
1277	    if (rule->keycodes)	_XkbFree(rule->keycodes);
1278	    if (rule->symbols)	_XkbFree(rule->symbols);
1279	    if (rule->types)	_XkbFree(rule->types);
1280	    if (rule->compat)	_XkbFree(rule->compat);
1281	    if (rule->geometry)	_XkbFree(rule->geometry);
1282	    bzero((char *)rule,sizeof(XkbRF_RuleRec));
1283	}
1284	_XkbFree(rules->rules);
1285	rules->num_rules= rules->sz_rules= 0;
1286	rules->rules= NULL;
1287    }
1288
1289    if (rules->groups) {
1290	for (i=0, group=rules->groups;i<rules->num_groups;i++,group++) {
1291	    if (group->name)	_XkbFree(group->name);
1292	    if (group->words)	_XkbFree(group->words);
1293	}
1294	_XkbFree(rules->groups);
1295	rules->num_groups= 0;
1296	rules->groups= NULL;
1297    }
1298    if (freeRules)
1299	_XkbFree(rules);
1300    return;
1301}
1302