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