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