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