1018 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1018 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
| /************************************************************
 | |
|  Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
 | |
| 
 | |
|  Permission to use, copy, modify, and distribute this
 | |
|  software and its documentation for any purpose and without
 | |
|  fee is hereby granted, provided that the above copyright
 | |
|  notice appear in all copies and that both that copyright
 | |
|  notice and this permission notice appear in supporting
 | |
|  documentation, and that the name of Silicon Graphics not be 
 | |
|  used in advertising or publicity pertaining to distribution 
 | |
|  of the software without specific prior written permission.
 | |
|  Silicon Graphics makes no representation about the suitability 
 | |
|  of this software for any purpose. It is provided "as is"
 | |
|  without any express or implied warranty.
 | |
|  
 | |
|  SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
 | |
|  SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
 | |
|  AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
 | |
|  GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
 | |
|  DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
 | |
|  DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
 | |
|  OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
 | |
|  THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | |
| 
 | |
|  ********************************************************/
 | |
| 
 | |
| #ifdef HAVE_DIX_CONFIG_H
 | |
| #include <dix-config.h>
 | |
| #endif
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <ctype.h>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #define X_INCLUDE_STRING_H
 | |
| #define XOS_USE_NO_LOCKING
 | |
| #include <X11/Xos_r.h>
 | |
| 
 | |
| #include <X11/Xproto.h>
 | |
| #include <X11/X.h>
 | |
| #include <X11/Xos.h>
 | |
| #include <X11/Xfuncs.h>
 | |
| #include <X11/Xatom.h>
 | |
| #include <X11/keysym.h>
 | |
| #include "misc.h"
 | |
| #include "inputstr.h"
 | |
| #include "dix.h"
 | |
| #include "os.h"
 | |
| #include "xkbstr.h"
 | |
| #define XKBSRV_NEED_FILE_FUNCS
 | |
| #include <xkbsrv.h>
 | |
| 
 | |
| /***====================================================================***/
 | |
| 
 | |
| 
 | |
| 
 | |
| #define DFLT_LINE_SIZE	128
 | |
| 
 | |
| typedef struct {
 | |
| 	int	line_num;
 | |
| 	int	sz_line;
 | |
| 	int	num_line;
 | |
| 	char	buf[DFLT_LINE_SIZE];
 | |
| 	char *	line;
 | |
| } InputLine;
 | |
| 
 | |
| static void
 | |
| InitInputLine(InputLine *line)
 | |
| {
 | |
|     line->line_num= 1;
 | |
|     line->num_line= 0;
 | |
|     line->sz_line= DFLT_LINE_SIZE;
 | |
|     line->line=	line->buf;
 | |
|     return;
 | |
| }
 | |
| 
 | |
| static void
 | |
| FreeInputLine(InputLine *line)
 | |
| {
 | |
|     if (line->line!=line->buf)
 | |
| 	free(line->line);
 | |
|     line->line_num= 1;
 | |
|     line->num_line= 0;
 | |
|     line->sz_line= DFLT_LINE_SIZE;
 | |
|     line->line= line->buf;
 | |
|     return;
 | |
| }
 | |
| 
 | |
| static int
 | |
| InputLineAddChar(InputLine *line,int ch)
 | |
| {
 | |
|     if (line->num_line>=line->sz_line) {
 | |
| 	if (line->line==line->buf) {
 | |
| 	    line->line= malloc(line->sz_line*2);
 | |
| 	    memcpy(line->line,line->buf,line->sz_line);
 | |
| 	}
 | |
| 	else {
 | |
| 	    line->line= realloc((char *)line->line,line->sz_line*2);
 | |
| 	}
 | |
| 	line->sz_line*= 2;
 | |
|     }
 | |
|     line->line[line->num_line++]= ch;
 | |
|     return ch;
 | |
| }
 | |
| 
 | |
| #define	ADD_CHAR(l,c)	((l)->num_line<(l)->sz_line?\
 | |
| 				(int)((l)->line[(l)->num_line++]= (c)):\
 | |
| 				InputLineAddChar(l,c))
 | |
| 
 | |
| static Bool
 | |
| GetInputLine(FILE *file,InputLine *line,Bool checkbang)
 | |
| {
 | |
| int	ch;
 | |
| Bool	endOfFile,spacePending,slashPending,inComment;
 | |
| 
 | |
|      endOfFile= FALSE;
 | |
|      while ((!endOfFile)&&(line->num_line==0)) {
 | |
| 	spacePending= slashPending= inComment= FALSE;
 | |
| 	while (((ch=getc(file))!='\n')&&(ch!=EOF)) {
 | |
| 	    if (ch=='\\') {
 | |
| 		if ((ch=getc(file))==EOF)
 | |
| 		    break;
 | |
| 		if (ch=='\n') {
 | |
| 		    inComment= FALSE;
 | |
| 		    ch= ' ';
 | |
| 		    line->line_num++;
 | |
| 		}
 | |
| 	    }
 | |
| 	    if (inComment)
 | |
| 		continue;
 | |
| 	    if (ch=='/') {
 | |
| 		if (slashPending) {
 | |
| 		    inComment= TRUE;
 | |
| 		    slashPending= FALSE;
 | |
| 		}
 | |
| 		else {
 | |
| 		    slashPending= TRUE;
 | |
| 		}
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    else if (slashPending) {
 | |
| 		if (spacePending) {
 | |
| 		    ADD_CHAR(line,' ');
 | |
| 		    spacePending= FALSE;
 | |
| 		}
 | |
| 		ADD_CHAR(line,'/');
 | |
| 		slashPending= FALSE;
 | |
| 	    }
 | |
| 	    if (isspace(ch)) {
 | |
| 		while (isspace(ch)&&(ch!='\n')&&(ch!=EOF)) {
 | |
| 		    ch= getc(file);
 | |
| 		}
 | |
| 		if (ch==EOF)
 | |
| 		    break;
 | |
| 		if ((ch!='\n')&&(line->num_line>0))
 | |
| 		    spacePending= TRUE;
 | |
| 		ungetc(ch,file);
 | |
| 	    }
 | |
| 	    else {
 | |
| 		if (spacePending) {
 | |
| 		    ADD_CHAR(line,' ');
 | |
| 		    spacePending= FALSE;
 | |
| 		}
 | |
| 		if (checkbang && ch=='!') {
 | |
| 		    if (line->num_line!=0) {
 | |
| 			DebugF("The '!' legal only at start of line\n");
 | |
| 			DebugF("Line containing '!' ignored\n");
 | |
| 			line->num_line= 0;
 | |
| 			inComment= 0;
 | |
| 			break;
 | |
| 		    }
 | |
| 
 | |
| 		}
 | |
| 		ADD_CHAR(line,ch);
 | |
| 	    }
 | |
| 	}
 | |
| 	if (ch==EOF)
 | |
| 	     endOfFile= TRUE;
 | |
| /*	else line->num_line++;*/
 | |
|      }
 | |
|      if ((line->num_line==0)&&(endOfFile))
 | |
| 	return FALSE;
 | |
|       ADD_CHAR(line,'\0');
 | |
|       return TRUE;
 | |
| }
 | |
| 
 | |
| /***====================================================================***/
 | |
| 
 | |
| #define	MODEL		0
 | |
| #define	LAYOUT		1
 | |
| #define	VARIANT		2
 | |
| #define	OPTION		3
 | |
| #define	KEYCODES	4
 | |
| #define SYMBOLS		5
 | |
| #define	TYPES		6
 | |
| #define	COMPAT		7
 | |
| #define	GEOMETRY	8
 | |
| #define	MAX_WORDS	9
 | |
| 
 | |
| #define	PART_MASK	0x000F
 | |
| #define	COMPONENT_MASK	0x03F0
 | |
| 
 | |
| static	const char *	cname[MAX_WORDS] = {
 | |
| 	"model", "layout", "variant", "option", 
 | |
| 	"keycodes", "symbols", "types", "compat", "geometry"
 | |
| };
 | |
| 
 | |
| typedef	struct _RemapSpec {
 | |
| 	int			number;
 | |
| 	int			num_remap;
 | |
| 	struct	{
 | |
| 		int	word;
 | |
| 		int	index;
 | |
|                 }		remap[MAX_WORDS];
 | |
| } RemapSpec;
 | |
| 
 | |
| typedef struct _FileSpec {
 | |
| 	char *			name[MAX_WORDS];
 | |
| 	struct _FileSpec *	pending;
 | |
| } FileSpec;
 | |
| 
 | |
| typedef struct {
 | |
| 	char *			model;
 | |
| 	char *			layout[XkbNumKbdGroups+1];
 | |
| 	char *			variant[XkbNumKbdGroups+1];
 | |
| 	char *			options;
 | |
| } XkbRF_MultiDefsRec, *XkbRF_MultiDefsPtr;
 | |
| 
 | |
| #define NDX_BUFF_SIZE	4
 | |
| 
 | |
| /***====================================================================***/
 | |
| 
 | |
| static char*
 | |
| get_index(char *str, int *ndx)
 | |
| {
 | |
|    char ndx_buf[NDX_BUFF_SIZE];
 | |
|    char *end;
 | |
| 
 | |
|    if (*str != '[') {
 | |
|        *ndx = 0;
 | |
|        return str;
 | |
|    }
 | |
|    str++;
 | |
|    end = strchr(str, ']');
 | |
|    if (end == NULL) {
 | |
|        *ndx = -1;
 | |
|        return str - 1;
 | |
|    }
 | |
|    if ( (end - str) >= NDX_BUFF_SIZE) {
 | |
|        *ndx = -1;
 | |
|        return end + 1;
 | |
|    }
 | |
|    strlcpy(ndx_buf, str, 1 + end - str);
 | |
|    *ndx = atoi(ndx_buf);
 | |
|    return end + 1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| SetUpRemap(InputLine *line,RemapSpec *remap)
 | |
| {
 | |
| char *		tok,*str;
 | |
| unsigned	present, l_ndx_present, v_ndx_present;
 | |
| register int	i;
 | |
| int		len, ndx;
 | |
| _Xstrtokparams	strtok_buf;
 | |
| Bool		found;
 | |
| 
 | |
| 
 | |
|    l_ndx_present = v_ndx_present = present= 0;
 | |
|    str= &line->line[1];
 | |
|    len = remap->number;
 | |
|    memset((char *)remap, 0, sizeof(RemapSpec));
 | |
|    remap->number = len;
 | |
|    while ((tok=_XStrtok(str," ",strtok_buf))!=NULL) {
 | |
| 	found= FALSE;
 | |
| 	str= NULL;
 | |
| 	if (strcmp(tok,"=")==0)
 | |
| 	    continue;
 | |
| 	for (i=0;i<MAX_WORDS;i++) {
 | |
|             len = strlen(cname[i]);
 | |
| 	    if (strncmp(cname[i],tok,len)==0) {
 | |
| 		if(strlen(tok) > len) {
 | |
| 		    char *end = get_index(tok+len, &ndx);
 | |
| 		    if ((i != LAYOUT && i != VARIANT) ||
 | |
| 			*end != '\0' || ndx == -1)
 | |
| 		        break;
 | |
| 		     if (ndx < 1 || ndx > XkbNumKbdGroups) {
 | |
| 		        DebugF("Illegal %s index: %d\n", cname[i], ndx);
 | |
| 		        DebugF("Index must be in range 1..%d\n",
 | |
| 				   XkbNumKbdGroups);
 | |
| 			break;
 | |
| 		     }
 | |
|                 } else {
 | |
| 		    ndx = 0;
 | |
|                 }
 | |
| 		found= TRUE;
 | |
| 		if (present&(1<<i)) {
 | |
| 		    if ((i == LAYOUT && l_ndx_present&(1<<ndx)) ||
 | |
| 			(i == VARIANT && v_ndx_present&(1<<ndx)) ) {
 | |
| 		        DebugF("Component \"%s\" listed twice\n",tok);
 | |
| 		        DebugF("Second definition ignored\n");
 | |
| 		        break;
 | |
| 		    }
 | |
| 		}
 | |
| 		present |= (1<<i);
 | |
|                 if (i == LAYOUT)
 | |
|                     l_ndx_present |= 1 << ndx;
 | |
|                 if (i == VARIANT)
 | |
|                     v_ndx_present |= 1 << ndx;
 | |
| 		remap->remap[remap->num_remap].word= i;
 | |
| 		remap->remap[remap->num_remap++].index= ndx;
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (!found) {
 | |
| 	    fprintf(stderr,"Unknown component \"%s\" ignored\n",tok);
 | |
| 	}
 | |
|    }
 | |
|    if ((present&PART_MASK)==0) {
 | |
| 	unsigned mask= PART_MASK;
 | |
| 	ErrorF("Mapping needs at least one of ");
 | |
| 	for (i=0; (i<MAX_WORDS); i++) {
 | |
| 	    if ((1L<<i)&mask) {
 | |
| 		mask&= ~(1L<<i);
 | |
| 		if (mask)	DebugF("\"%s,\" ",cname[i]);
 | |
| 		else		DebugF("or \"%s\"\n",cname[i]);
 | |
| 	    }
 | |
| 	}
 | |
| 	DebugF("Illegal mapping ignored\n");
 | |
| 	remap->num_remap= 0;
 | |
| 	return;
 | |
|    }
 | |
|    if ((present&COMPONENT_MASK)==0) {
 | |
| 	DebugF("Mapping needs at least one component\n");
 | |
| 	DebugF("Illegal mapping ignored\n");
 | |
| 	remap->num_remap= 0;
 | |
| 	return;
 | |
|    }
 | |
|    remap->number++;
 | |
|    return;
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| MatchOneOf(char *wanted,char *vals_defined)
 | |
| {
 | |
| char	*str,*next;
 | |
| int	want_len= strlen(wanted);
 | |
| 
 | |
|     for (str=vals_defined,next=NULL;str!=NULL;str=next) {
 | |
| 	int len;
 | |
| 	next= strchr(str,',');
 | |
| 	if (next) {
 | |
| 	    len= next-str;
 | |
| 	    next++;
 | |
| 	}
 | |
| 	else {
 | |
| 	    len= strlen(str);
 | |
| 	}
 | |
| 	if ((len==want_len)&&(strncmp(wanted,str,len)==0))
 | |
| 	    return TRUE;
 | |
|     }
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| /***====================================================================***/
 | |
| 
 | |
| static Bool
 | |
| CheckLine(	InputLine *		line,
 | |
| 		RemapSpec *		remap,
 | |
| 		XkbRF_RulePtr		rule,
 | |
| 		XkbRF_GroupPtr		group)
 | |
| {
 | |
| char *		str,*tok;
 | |
| register int	nread, i;
 | |
| FileSpec	tmp;
 | |
| _Xstrtokparams	strtok_buf;
 | |
| Bool 		append = FALSE;
 | |
| 
 | |
|     if (line->line[0]=='!') {
 | |
|         if (line->line[1] == '$' ||
 | |
|             (line->line[1] == ' ' && line->line[2] == '$')) {
 | |
|             char *gname = strchr(line->line, '$');
 | |
|             char *words = strchr(gname, ' ');
 | |
|             if(!words)
 | |
|                 return FALSE;
 | |
|             *words++ = '\0';
 | |
|             for (; *words; words++) {
 | |
|                 if (*words != '=' && *words != ' ')
 | |
|                     break;
 | |
|             }
 | |
|             if (*words == '\0')
 | |
|                 return FALSE;
 | |
|             group->name = Xstrdup(gname);
 | |
|             group->words = Xstrdup(words);
 | |
|             for (i = 1, words = group->words; *words; words++) {
 | |
|                  if ( *words == ' ') {
 | |
|                      *words++ = '\0';
 | |
|                      i++;
 | |
|                  }
 | |
|             }
 | |
|             group->number = i;
 | |
|             return TRUE;
 | |
|         } else {
 | |
| 	    SetUpRemap(line,remap);
 | |
| 	    return FALSE;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (remap->num_remap==0) {
 | |
| 	DebugF("Must have a mapping before first line of data\n");
 | |
| 	DebugF("Illegal line of data ignored\n");
 | |
| 	return FALSE;
 | |
|     }
 | |
|     memset((char *)&tmp, 0, sizeof(FileSpec));
 | |
|     str= line->line;
 | |
|     for (nread= 0;(tok=_XStrtok(str," ",strtok_buf))!=NULL;nread++) {
 | |
| 	str= NULL;
 | |
| 	if (strcmp(tok,"=")==0) {
 | |
| 	    nread--;
 | |
| 	    continue;
 | |
| 	}
 | |
| 	if (nread>remap->num_remap) {
 | |
| 	    DebugF("Too many words on a line\n");
 | |
| 	    DebugF("Extra word \"%s\" ignored\n",tok);
 | |
| 	    continue;
 | |
| 	}
 | |
| 	tmp.name[remap->remap[nread].word]= tok;
 | |
| 	if (*tok == '+' || *tok == '|')
 | |
| 	    append = TRUE;
 | |
|     }
 | |
|     if (nread<remap->num_remap) {
 | |
| 	DebugF("Too few words on a line: %s\n", line->line);
 | |
| 	DebugF("line ignored\n");
 | |
| 	return FALSE;
 | |
|     }
 | |
| 
 | |
|     rule->flags= 0;
 | |
|     rule->number = remap->number;
 | |
|     if (tmp.name[OPTION])
 | |
| 	 rule->flags|= XkbRF_Option;
 | |
|     else if (append)
 | |
| 	 rule->flags|= XkbRF_Append;
 | |
|     else
 | |
| 	 rule->flags|= XkbRF_Normal;
 | |
|     rule->model= Xstrdup(tmp.name[MODEL]);
 | |
|     rule->layout= Xstrdup(tmp.name[LAYOUT]);
 | |
|     rule->variant= Xstrdup(tmp.name[VARIANT]);
 | |
|     rule->option= Xstrdup(tmp.name[OPTION]);
 | |
| 
 | |
|     rule->keycodes= Xstrdup(tmp.name[KEYCODES]);
 | |
|     rule->symbols= Xstrdup(tmp.name[SYMBOLS]);
 | |
|     rule->types= Xstrdup(tmp.name[TYPES]);
 | |
|     rule->compat= Xstrdup(tmp.name[COMPAT]);
 | |
|     rule->geometry= Xstrdup(tmp.name[GEOMETRY]);
 | |
| 
 | |
|     rule->layout_num = rule->variant_num = 0;
 | |
|     for (i = 0; i < nread; i++) {
 | |
|         if (remap->remap[i].index) {
 | |
| 	    if (remap->remap[i].word == LAYOUT)
 | |
| 	        rule->layout_num = remap->remap[i].index;
 | |
| 	    if (remap->remap[i].word == VARIANT)
 | |
| 	        rule->variant_num = remap->remap[i].index;
 | |
|         }
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static char *
 | |
| _Concat(char *str1,char *str2)
 | |
| {
 | |
| int len;
 | |
| 
 | |
|     if ((!str1)||(!str2))
 | |
| 	return str1;
 | |
|     len= strlen(str1)+strlen(str2)+1;
 | |
|     str1= realloc(str1,len * sizeof(char));
 | |
|     if (str1)
 | |
| 	strcat(str1,str2);
 | |
|     return str1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| squeeze_spaces(char *p1)
 | |
| {
 | |
|    char *p2;
 | |
|    for (p2 = p1; *p2; p2++) {
 | |
|        *p1 = *p2;
 | |
|        if (*p1 != ' ') p1++;
 | |
|    }
 | |
|    *p1 = '\0';
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| MakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs)
 | |
| {
 | |
| 
 | |
|    memset((char *)mdefs, 0, sizeof(XkbRF_MultiDefsRec));
 | |
|    mdefs->model = defs->model;
 | |
|    mdefs->options = Xstrdup(defs->options);
 | |
|    if (mdefs->options) squeeze_spaces(mdefs->options); 
 | |
| 
 | |
|    if (defs->layout) {
 | |
|        if (!strchr(defs->layout, ',')) {
 | |
|            mdefs->layout[0] = defs->layout;
 | |
|        } else {
 | |
|            char *p;
 | |
|            int i;
 | |
|            mdefs->layout[1] = Xstrdup(defs->layout);
 | |
| 	   if (mdefs->layout[1] == NULL)
 | |
| 	      return FALSE;
 | |
|            squeeze_spaces(mdefs->layout[1]);
 | |
|            p = mdefs->layout[1];
 | |
|            for (i = 2; i <= XkbNumKbdGroups; i++) {
 | |
|               if ((p = strchr(p, ','))) {
 | |
|                  *p++ = '\0';
 | |
|                  mdefs->layout[i] = p;
 | |
|               } else {
 | |
|                  break;
 | |
|               }
 | |
|            }
 | |
|            if (p && (p = strchr(p, ',')))
 | |
|               *p = '\0';
 | |
|        }
 | |
|    }
 | |
| 
 | |
|    if (defs->variant) {
 | |
|        if (!strchr(defs->variant, ',')) {
 | |
|            mdefs->variant[0] = defs->variant;
 | |
|        } else {
 | |
|            char *p;
 | |
|            int i;
 | |
|            mdefs->variant[1] = Xstrdup(defs->variant);
 | |
| 	   if (mdefs->variant[1] == NULL)
 | |
| 	      return FALSE;
 | |
|            squeeze_spaces(mdefs->variant[1]);
 | |
|            p = mdefs->variant[1];
 | |
|            for (i = 2; i <= XkbNumKbdGroups; i++) {
 | |
|               if ((p = strchr(p, ','))) {
 | |
|                  *p++ = '\0';
 | |
|                  mdefs->variant[i] = p;
 | |
|               } else {
 | |
|                  break;
 | |
|               }
 | |
|            }
 | |
|            if (p && (p = strchr(p, ',')))
 | |
|               *p = '\0';
 | |
|        }
 | |
|    }
 | |
|    return TRUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| FreeMultiDefs(XkbRF_MultiDefsPtr defs)
 | |
| {
 | |
|   free(defs->options);
 | |
|   free(defs->layout[1]);
 | |
|   free(defs->variant[1]);
 | |
| }
 | |
| 
 | |
| static void
 | |
| Apply(char *src, char **dst)
 | |
| {
 | |
|     if (src) {
 | |
|         if (*src == '+' || *src == '!') {
 | |
| 	    *dst= _Concat(*dst, src);
 | |
|         } else {
 | |
|             if (*dst == NULL)
 | |
| 	        *dst= Xstrdup(src);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| XkbRF_ApplyRule(	XkbRF_RulePtr 		rule,
 | |
| 			XkbComponentNamesPtr	names)
 | |
| {
 | |
|     rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
 | |
| 
 | |
|     Apply(rule->keycodes, &names->keycodes);
 | |
|     Apply(rule->symbols,  &names->symbols);
 | |
|     Apply(rule->types,    &names->types);
 | |
|     Apply(rule->compat,   &names->compat);
 | |
|     Apply(rule->geometry, &names->geometry);
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| CheckGroup(	XkbRF_RulesPtr          rules,
 | |
| 		char * 			group_name,
 | |
| 		char * 			name)
 | |
| {
 | |
|    int i;
 | |
|    char *p;
 | |
|    XkbRF_GroupPtr group;
 | |
| 
 | |
|    for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
 | |
|        if (! strcmp(group->name, group_name)) {
 | |
|            break;
 | |
|        }
 | |
|    }
 | |
|    if (i == rules->num_groups)
 | |
|        return FALSE;
 | |
|    for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
 | |
|        if (! strcmp(p, name)) {
 | |
|            return TRUE;
 | |
|        }
 | |
|    }
 | |
|    return FALSE;
 | |
| }
 | |
| 
 | |
| static int
 | |
| XkbRF_CheckApplyRule(	XkbRF_RulePtr 		rule,
 | |
| 			XkbRF_MultiDefsPtr	mdefs,
 | |
| 			XkbComponentNamesPtr	names,
 | |
| 			XkbRF_RulesPtr          rules)
 | |
| {
 | |
|     Bool pending = FALSE;
 | |
| 
 | |
|     if (rule->model != NULL) {
 | |
|         if(mdefs->model == NULL)
 | |
|             return 0;
 | |
|         if (strcmp(rule->model, "*") == 0) {
 | |
|             pending = TRUE;
 | |
|         } else {
 | |
|             if (rule->model[0] == '$') {
 | |
|                if (!CheckGroup(rules, rule->model, mdefs->model))
 | |
|                   return 0;
 | |
|             } else {
 | |
| 	       if (strcmp(rule->model, mdefs->model) != 0)
 | |
| 	          return 0;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     if (rule->option != NULL) {
 | |
| 	if (mdefs->options == NULL)
 | |
| 	    return 0;
 | |
| 	if ((!MatchOneOf(rule->option,mdefs->options)))
 | |
| 	    return 0;
 | |
|     }
 | |
| 
 | |
|     if (rule->layout != NULL) {
 | |
| 	if(mdefs->layout[rule->layout_num] == NULL ||
 | |
| 	   *mdefs->layout[rule->layout_num] == '\0')
 | |
| 	    return 0;
 | |
|         if (strcmp(rule->layout, "*") == 0) {
 | |
|             pending = TRUE;
 | |
|         } else {
 | |
|             if (rule->layout[0] == '$') {
 | |
|                if (!CheckGroup(rules, rule->layout,
 | |
|                                mdefs->layout[rule->layout_num]))
 | |
|                   return 0;
 | |
| 	    } else {
 | |
| 	       if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
 | |
| 	           return 0;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     if (rule->variant != NULL) {
 | |
| 	if (mdefs->variant[rule->variant_num] == NULL ||
 | |
| 	    *mdefs->variant[rule->variant_num] == '\0')
 | |
| 	    return 0;
 | |
|         if (strcmp(rule->variant, "*") == 0) {
 | |
|             pending = TRUE;
 | |
|         } else {
 | |
|             if (rule->variant[0] == '$') {
 | |
|                if (!CheckGroup(rules, rule->variant,
 | |
|                                mdefs->variant[rule->variant_num]))
 | |
|                   return 0;
 | |
|             } else {
 | |
| 	       if (strcmp(rule->variant,
 | |
|                           mdefs->variant[rule->variant_num]) != 0)
 | |
| 	           return 0;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     if (pending) {
 | |
|         rule->flags|= XkbRF_PendingMatch;
 | |
| 	return rule->number;
 | |
|     }
 | |
|     /* exact match, apply it now */
 | |
|     XkbRF_ApplyRule(rule,names);
 | |
|     return rule->number;
 | |
| }
 | |
| 
 | |
| static void
 | |
| XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
 | |
| {
 | |
| register int 	i;
 | |
| XkbRF_RulePtr	rule;
 | |
| 
 | |
|     for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
 | |
| 	rule->flags&= ~XkbRF_PendingMatch;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules,XkbComponentNamesPtr names)
 | |
| {
 | |
| int		i;
 | |
| XkbRF_RulePtr	rule;
 | |
| 
 | |
|     for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
 | |
| 	if ((rule->flags&XkbRF_PendingMatch)==0)
 | |
| 	    continue;
 | |
| 	XkbRF_ApplyRule(rule,names);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| XkbRF_CheckApplyRules(	XkbRF_RulesPtr 		rules,
 | |
| 			XkbRF_MultiDefsPtr	mdefs,
 | |
| 			XkbComponentNamesPtr	names,
 | |
| 			int			flags)
 | |
| {
 | |
| int		i;
 | |
| XkbRF_RulePtr	rule;
 | |
| int		skip;
 | |
| 
 | |
|     for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
 | |
| 	if ((rule->flags & flags) != flags)
 | |
| 	    continue;
 | |
| 	skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
 | |
| 	if (skip && !(flags & XkbRF_Option)) {
 | |
| 	    for ( ;(i < rules->num_rules) && (rule->number == skip);
 | |
| 		  rule++, i++);
 | |
| 	    rule--; i--;
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /***====================================================================***/
 | |
| 
 | |
| static char *
 | |
| XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
 | |
| {
 | |
| char 	*str, *outstr, *orig, *var;
 | |
| int	len, ndx;
 | |
| 
 | |
|     orig= name;
 | |
|     str= index(name,'%');
 | |
|     if (str==NULL)
 | |
| 	return name;
 | |
|     len= strlen(name);
 | |
|     while (str!=NULL) {
 | |
| 	char pfx= str[1];
 | |
| 	int   extra_len= 0;
 | |
| 	if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
 | |
| 	    extra_len= 1;
 | |
| 	    str++;
 | |
| 	}
 | |
| 	else if (pfx=='(') {
 | |
| 	    extra_len= 2;
 | |
| 	    str++;
 | |
| 	}
 | |
| 	var = str + 1;
 | |
| 	str = get_index(var + 1, &ndx);
 | |
| 	if (ndx == -1) {
 | |
| 	    str = index(str,'%');
 | |
| 	    continue;
 | |
|         }
 | |
| 	if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
 | |
| 	    len+= strlen(mdefs->layout[ndx])+extra_len;
 | |
| 	else if ((*var=='m')&&mdefs->model)
 | |
| 	    len+= strlen(mdefs->model)+extra_len;
 | |
| 	else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
 | |
| 	    len+= strlen(mdefs->variant[ndx])+extra_len;
 | |
| 	if ((pfx=='(')&&(*str==')')) {
 | |
| 	    str++;
 | |
| 	}
 | |
| 	str= index(&str[0],'%');
 | |
|     }
 | |
|     name= malloc(len+1);
 | |
|     str= orig;
 | |
|     outstr= name;
 | |
|     while (*str!='\0') {
 | |
| 	if (str[0]=='%') {
 | |
| 	    char pfx,sfx;
 | |
| 	    str++;
 | |
| 	    pfx= str[0];
 | |
| 	    sfx= '\0';
 | |
| 	    if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
 | |
| 		str++;
 | |
| 	    }
 | |
| 	    else if (pfx=='(') {
 | |
| 		sfx= ')';
 | |
| 		str++;
 | |
| 	    }
 | |
| 	    else pfx= '\0';
 | |
| 
 | |
| 	    var = str;
 | |
| 	    str = get_index(var + 1, &ndx);
 | |
| 	    if (ndx == -1) {
 | |
| 	        continue;
 | |
|             }
 | |
| 	    if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
 | |
| 		if (pfx) *outstr++= pfx;
 | |
| 		strcpy(outstr,mdefs->layout[ndx]);
 | |
| 		outstr+= strlen(mdefs->layout[ndx]);
 | |
| 		if (sfx) *outstr++= sfx;
 | |
| 	    }
 | |
| 	    else if ((*var=='m')&&(mdefs->model)) {
 | |
| 		if (pfx) *outstr++= pfx;
 | |
| 		strcpy(outstr,mdefs->model);
 | |
| 		outstr+= strlen(mdefs->model);
 | |
| 		if (sfx) *outstr++= sfx;
 | |
| 	    }
 | |
| 	    else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
 | |
| 		if (pfx) *outstr++= pfx;
 | |
| 		strcpy(outstr,mdefs->variant[ndx]);
 | |
| 		outstr+= strlen(mdefs->variant[ndx]);
 | |
| 		if (sfx) *outstr++= sfx;
 | |
| 	    }
 | |
| 	    if ((pfx=='(')&&(*str==')'))
 | |
| 		str++;
 | |
| 	}
 | |
| 	else {
 | |
| 	    *outstr++= *str++;
 | |
| 	}
 | |
|     }
 | |
|     *outstr++= '\0';
 | |
|     if (orig!=name)
 | |
| 	free(orig);
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| /***====================================================================***/
 | |
| 
 | |
| Bool
 | |
| XkbRF_GetComponents(	XkbRF_RulesPtr		rules,
 | |
| 			XkbRF_VarDefsPtr	defs,
 | |
| 			XkbComponentNamesPtr	names)
 | |
| {
 | |
|     XkbRF_MultiDefsRec mdefs;
 | |
| 
 | |
|     MakeMultiDefs(&mdefs, defs);
 | |
| 
 | |
|     memset((char *)names, 0, sizeof(XkbComponentNamesRec));
 | |
|     XkbRF_ClearPartialMatches(rules);
 | |
|     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
 | |
|     XkbRF_ApplyPartialMatches(rules, names);
 | |
|     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
 | |
|     XkbRF_ApplyPartialMatches(rules, names);
 | |
|     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
 | |
| 
 | |
|     if (names->keycodes)
 | |
| 	names->keycodes= XkbRF_SubstituteVars(names->keycodes, &mdefs);
 | |
|     if (names->symbols)	
 | |
| 	names->symbols=	XkbRF_SubstituteVars(names->symbols, &mdefs);
 | |
|     if (names->types)
 | |
| 	names->types= XkbRF_SubstituteVars(names->types, &mdefs);
 | |
|     if (names->compat)
 | |
| 	names->compat= XkbRF_SubstituteVars(names->compat, &mdefs);
 | |
|     if (names->geometry)
 | |
| 	names->geometry= XkbRF_SubstituteVars(names->geometry, &mdefs);
 | |
| 
 | |
|     FreeMultiDefs(&mdefs);
 | |
|     return (names->keycodes && names->symbols && names->types &&
 | |
| 		names->compat && names->geometry);
 | |
| }
 | |
| 
 | |
| static XkbRF_RulePtr
 | |
| XkbRF_AddRule(XkbRF_RulesPtr	rules)
 | |
| {
 | |
|     if (rules->sz_rules<1) {
 | |
| 	rules->sz_rules= 16;
 | |
| 	rules->num_rules= 0;
 | |
| 	rules->rules= calloc(rules->sz_rules, sizeof(XkbRF_RuleRec));
 | |
|     }
 | |
|     else if (rules->num_rules>=rules->sz_rules) {
 | |
| 	rules->sz_rules*= 2;
 | |
| 	rules->rules= realloc(rules->rules,
 | |
| 				rules->sz_rules * sizeof(XkbRF_RuleRec));
 | |
|     }
 | |
|     if (!rules->rules) {
 | |
| 	rules->sz_rules= rules->num_rules= 0;
 | |
| 	DebugF("Allocation failure in XkbRF_AddRule\n");
 | |
| 	return NULL;
 | |
|     }
 | |
|     memset((char *)&rules->rules[rules->num_rules], 0, sizeof(XkbRF_RuleRec));
 | |
|     return &rules->rules[rules->num_rules++];
 | |
| }
 | |
| 
 | |
| static XkbRF_GroupPtr
 | |
| XkbRF_AddGroup(XkbRF_RulesPtr	rules)
 | |
| {
 | |
|     if (rules->sz_groups<1) {
 | |
| 	rules->sz_groups= 16;
 | |
| 	rules->num_groups= 0;
 | |
| 	rules->groups= calloc(rules->sz_groups, sizeof(XkbRF_GroupRec));
 | |
|     }
 | |
|     else if (rules->num_groups >= rules->sz_groups) {
 | |
| 	rules->sz_groups *= 2;
 | |
| 	rules->groups= realloc(rules->groups,
 | |
| 				rules->sz_groups * sizeof(XkbRF_GroupRec));
 | |
|     }
 | |
|     if (!rules->groups) {
 | |
| 	rules->sz_groups= rules->num_groups= 0;
 | |
| 	return NULL;
 | |
|     }
 | |
| 
 | |
|     memset((char *)&rules->groups[rules->num_groups], 0, sizeof(XkbRF_GroupRec));
 | |
|     return &rules->groups[rules->num_groups++];
 | |
| }
 | |
| 
 | |
| Bool
 | |
| XkbRF_LoadRules(FILE *file, XkbRF_RulesPtr rules)
 | |
| {
 | |
| InputLine	line;
 | |
| RemapSpec	remap;
 | |
| XkbRF_RuleRec	trule,*rule;
 | |
| XkbRF_GroupRec  tgroup,*group;
 | |
| 
 | |
|     if (!(rules && file))
 | |
| 	return FALSE;
 | |
|     memset((char *)&remap, 0, sizeof(RemapSpec));
 | |
|     memset((char *)&tgroup, 0, sizeof(XkbRF_GroupRec));
 | |
|     InitInputLine(&line);
 | |
|     while (GetInputLine(file,&line,TRUE)) {
 | |
| 	if (CheckLine(&line,&remap,&trule,&tgroup)) {
 | |
|             if (tgroup.number) {
 | |
| 	        if ((group= XkbRF_AddGroup(rules))!=NULL) {
 | |
| 		    *group= tgroup;
 | |
| 		    memset((char *)&tgroup, 0, sizeof(XkbRF_GroupRec));
 | |
| 	        }
 | |
| 	    } else {
 | |
| 	        if ((rule= XkbRF_AddRule(rules))!=NULL) {
 | |
| 		    *rule= trule;
 | |
| 		    memset((char *)&trule, 0, sizeof(XkbRF_RuleRec));
 | |
| 	        }
 | |
| 	    }
 | |
| 	}
 | |
| 	line.num_line= 0;
 | |
|     }
 | |
|     FreeInputLine(&line);
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| XkbRF_LoadRulesByName(char *base,char *locale,XkbRF_RulesPtr rules)
 | |
| {
 | |
| FILE *		file;
 | |
| char		buf[PATH_MAX];
 | |
| Bool		ok;
 | |
| 
 | |
|     if ((!base)||(!rules))
 | |
| 	return FALSE;
 | |
|     if (locale) {
 | |
| 	if (snprintf(buf, PATH_MAX, "%s-%s", base, locale) >= PATH_MAX)
 | |
| 	    return FALSE;
 | |
|     }
 | |
|     else {
 | |
| 	if (strlen(base)+1 > PATH_MAX)
 | |
| 	    return FALSE;
 | |
| 	strcpy(buf,base);
 | |
|     }
 | |
| 
 | |
|     file= fopen(buf, "r");
 | |
|     if ((!file)&&(locale)) { /* fallback if locale was specified */
 | |
| 	strcpy(buf,base);
 | |
| 	file= fopen(buf, "r");
 | |
|     }
 | |
|     if (!file)
 | |
| 	return FALSE;
 | |
|     ok= XkbRF_LoadRules(file,rules);
 | |
|     fclose(file);
 | |
|     return ok;
 | |
| }
 | |
| 
 | |
| /***====================================================================***/
 | |
| 
 | |
| XkbRF_RulesPtr
 | |
| XkbRF_Create(void)
 | |
| {
 | |
|     return calloc(1, sizeof( XkbRF_RulesRec));
 | |
| }
 | |
| 
 | |
| /***====================================================================***/
 | |
| 
 | |
| void
 | |
| XkbRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
 | |
| {
 | |
| int		i;
 | |
| XkbRF_RulePtr	rule;
 | |
| XkbRF_GroupPtr	group;
 | |
| 
 | |
|     if (!rules)
 | |
| 	return;
 | |
|     if (rules->rules) {
 | |
| 	for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
 | |
| 	    free(rule->model);
 | |
| 	    free(rule->layout);
 | |
| 	    free(rule->variant);
 | |
| 	    free(rule->option);
 | |
| 	    free(rule->keycodes);
 | |
| 	    free(rule->symbols);
 | |
| 	    free(rule->types);
 | |
| 	    free(rule->compat);
 | |
| 	    free(rule->geometry);
 | |
| 	    memset((char *)rule, 0, sizeof(XkbRF_RuleRec));
 | |
| 	}
 | |
| 	free(rules->rules);
 | |
| 	rules->num_rules= rules->sz_rules= 0;
 | |
| 	rules->rules= NULL;
 | |
|     }
 | |
| 
 | |
|     if (rules->groups) {
 | |
| 	for (i=0, group=rules->groups;i<rules->num_groups;i++,group++) {
 | |
| 	    free(group->name);
 | |
| 	    free(group->words);
 | |
| 	}
 | |
| 	free(rules->groups);
 | |
| 	rules->num_groups= 0;
 | |
| 	rules->groups= NULL;
 | |
|     }
 | |
|     if (freeRules)
 | |
| 	free(rules);
 | |
|     return;
 | |
| }
 |