#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>
#define NEED_EVENTS
#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)
_XkbFree(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= (char *)_XkbAlloc(line->sz_line*2);
memcpy(line->line,line->buf,line->sz_line);
}
else {
line->line=(char *)_XkbRealloc((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;
}
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 KEYMAP 9
#define MAX_WORDS 10
#define PART_MASK 0x000F
#define COMPONENT_MASK 0x03F0
static char * cname[MAX_WORDS] = {
"model", "layout", "variant", "option",
"keycodes", "symbols", "types", "compat", "geometry", "keymap"
};
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;
}
strncpy(ndx_buf, str, end - str);
ndx_buf[end - str] = '\0';
*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;
bzero((char *)remap,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;
}
if (((present&COMPONENT_MASK)&(1<<KEYMAP))&&
((present&COMPONENT_MASK)!=(1<<KEYMAP))) {
DebugF("Keymap cannot appear with other components\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 = _XkbDupString(gname);
group->words = _XkbDupString(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;
}
bzero((char *)&tmp,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= _XkbDupString(tmp.name[MODEL]);
rule->layout= _XkbDupString(tmp.name[LAYOUT]);
rule->variant= _XkbDupString(tmp.name[VARIANT]);
rule->option= _XkbDupString(tmp.name[OPTION]);
rule->keycodes= _XkbDupString(tmp.name[KEYCODES]);
rule->symbols= _XkbDupString(tmp.name[SYMBOLS]);
rule->types= _XkbDupString(tmp.name[TYPES]);
rule->compat= _XkbDupString(tmp.name[COMPAT]);
rule->geometry= _XkbDupString(tmp.name[GEOMETRY]);
rule->keymap= NULL;
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= _XkbTypedRealloc(str1,len,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)
{
bzero((char *)mdefs,sizeof(XkbRF_MultiDefsRec));
mdefs->model = defs->model;
mdefs->options = _XkbDupString(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] = _XkbDupString(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] = _XkbDupString(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)
{
if (defs->options) _XkbFree(defs->options);
if (defs->layout[1]) _XkbFree(defs->layout[1]);
if (defs->variant[1]) _XkbFree(defs->variant[1]);
}
static void
Apply(char *src, char **dst)
{
if (src) {
if (*src == '+' || *src == '!') {
*dst= _Concat(*dst, src);
} else {
if (*dst == NULL)
*dst= _XkbDupString(src);
}
}
}
static void
XkbRF_ApplyRule( XkbRF_RulePtr rule,
XkbComponentNamesPtr names)
{
rule->flags&= ~XkbRF_PendingMatch;
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;
}
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= (char *)_XkbAlloc(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)
_XkbFree(orig);
return name;
}
Bool
XkbRF_GetComponents( XkbRF_RulesPtr rules,
XkbRF_VarDefsPtr defs,
XkbComponentNamesPtr names)
{
XkbRF_MultiDefsRec mdefs;
MakeMultiDefs(&mdefs, defs);
bzero((char *)names,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);
if (names->keymap)
names->keymap= XkbRF_SubstituteVars(names->keymap, &mdefs);
FreeMultiDefs(&mdefs);
return (names->keycodes && names->symbols && names->types &&
names->compat && names->geometry ) || names->keymap;
}
XkbRF_RulePtr
XkbRF_AddRule(XkbRF_RulesPtr rules)
{
if (rules->sz_rules<1) {
rules->sz_rules= 16;
rules->num_rules= 0;
rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
}
else if (rules->num_rules>=rules->sz_rules) {
rules->sz_rules*= 2;
rules->rules= _XkbTypedRealloc(rules->rules,rules->sz_rules,
XkbRF_RuleRec);
}
if (!rules->rules) {
rules->sz_rules= rules->num_rules= 0;
DebugF("Allocation failure in XkbRF_AddRule\n");
return NULL;
}
bzero((char *)&rules->rules[rules->num_rules],sizeof(XkbRF_RuleRec));
return &rules->rules[rules->num_rules++];
}
XkbRF_GroupPtr
XkbRF_AddGroup(XkbRF_RulesPtr rules)
{
if (rules->sz_groups<1) {
rules->sz_groups= 16;
rules->num_groups= 0;
rules->groups= _XkbTypedCalloc(rules->sz_groups,XkbRF_GroupRec);
}
else if (rules->num_groups >= rules->sz_groups) {
rules->sz_groups *= 2;
rules->groups= _XkbTypedRealloc(rules->groups,rules->sz_groups,
XkbRF_GroupRec);
}
if (!rules->groups) {
rules->sz_groups= rules->num_groups= 0;
return NULL;
}
bzero((char *)&rules->groups[rules->num_groups],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;
bzero((char *)&remap,sizeof(RemapSpec));
bzero((char *)&tgroup,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;
bzero((char *)&tgroup,sizeof(XkbRF_GroupRec));
}
} else {
if ((rule= XkbRF_AddRule(rules))!=NULL) {
*rule= trule;
bzero((char *)&trule,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 (strlen(base)+strlen(locale)+2 > PATH_MAX)
return False;
sprintf(buf,"%s-%s", base, locale);
}
else {
if (strlen(base)+1 > PATH_MAX)
return False;
strcpy(buf,base);
}
file= fopen(buf, "r");
if ((!file)&&(locale)) {
strcpy(buf,base);
file= fopen(buf, "r");
}
if (!file)
return False;
ok= XkbRF_LoadRules(file,rules);
fclose(file);
return ok;
}
#define HEAD_NONE 0
#define HEAD_MODEL 1
#define HEAD_LAYOUT 2
#define HEAD_VARIANT 3
#define HEAD_OPTION 4
#define HEAD_EXTRA 5
XkbRF_VarDescPtr
XkbRF_AddVarDesc(XkbRF_DescribeVarsPtr vars)
{
if (vars->sz_desc<1) {
vars->sz_desc= 16;
vars->num_desc= 0;
vars->desc= _XkbTypedCalloc(vars->sz_desc,XkbRF_VarDescRec);
}
else if (vars->num_desc>=vars->sz_desc) {
vars->sz_desc*= 2;
vars->desc= _XkbTypedRealloc(vars->desc,vars->sz_desc,XkbRF_VarDescRec);
}
if (!vars->desc) {
vars->sz_desc= vars->num_desc= 0;
DebugF("Allocation failure in XkbRF_AddVarDesc\n");
return NULL;
}
vars->desc[vars->num_desc].name= NULL;
vars->desc[vars->num_desc].desc= NULL;
return &vars->desc[vars->num_desc++];
}
XkbRF_VarDescPtr
XkbRF_AddVarDescCopy(XkbRF_DescribeVarsPtr vars,XkbRF_VarDescPtr from)
{
XkbRF_VarDescPtr nd;
if ((nd=XkbRF_AddVarDesc(vars))!=NULL) {
nd->name= _XkbDupString(from->name);
nd->desc= _XkbDupString(from->desc);
}
return nd;
}
XkbRF_DescribeVarsPtr
XkbRF_AddVarToDescribe(XkbRF_RulesPtr rules,char *name)
{
if (rules->sz_extra<1) {
rules->num_extra= 0;
rules->sz_extra= 1;
rules->extra_names= _XkbTypedCalloc(rules->sz_extra,char *);
rules->extra= _XkbTypedCalloc(rules->sz_extra, XkbRF_DescribeVarsRec);
}
else if (rules->num_extra>=rules->sz_extra) {
rules->sz_extra*= 2;
rules->extra_names= _XkbTypedRealloc(rules->extra_names,rules->sz_extra,
char *);
rules->extra=_XkbTypedRealloc(rules->extra, rules->sz_extra,
XkbRF_DescribeVarsRec);
}
if ((!rules->extra_names)||(!rules->extra)) {
DebugF("allocation error in extra parts\n");
rules->sz_extra= rules->num_extra= 0;
rules->extra_names= NULL;
rules->extra= NULL;
return NULL;
}
rules->extra_names[rules->num_extra]= _XkbDupString(name);
bzero(&rules->extra[rules->num_extra],sizeof(XkbRF_DescribeVarsRec));
return &rules->extra[rules->num_extra++];
}
Bool
XkbRF_LoadDescriptions(FILE *file,XkbRF_RulesPtr rules)
{
InputLine line;
XkbRF_VarDescRec tmp;
char *tok;
int len,headingtype,extra_ndx = 0;
bzero((char *)&tmp, sizeof(XkbRF_VarDescRec));
headingtype = HEAD_NONE;
InitInputLine(&line);
for ( ; GetInputLine(file,&line,False); line.num_line= 0) {
if (line.line[0]=='!') {
tok = strtok(&(line.line[1]), " \t");
if (strcasecmp(tok,"model") == 0)
headingtype = HEAD_MODEL;
else if (strcasecmp(tok,"layout") == 0)
headingtype = HEAD_LAYOUT;
else if (strcasecmp(tok,"variant") == 0)
headingtype = HEAD_VARIANT;
else if (strcasecmp(tok,"option") == 0)
headingtype = HEAD_OPTION;
else {
int i;
headingtype = HEAD_EXTRA;
extra_ndx= -1;
for (i=0;(i<rules->num_extra)&&(extra_ndx<0);i++) {
if (!strcasecmp(tok,rules->extra_names[i]))
extra_ndx= i;
}
if (extra_ndx<0) {
XkbRF_DescribeVarsPtr var;
DebugF("Extra heading \"%s\" encountered\n",tok);
var= XkbRF_AddVarToDescribe(rules,tok);
if (var)
extra_ndx= var-rules->extra;
else headingtype= HEAD_NONE;
}
}
continue;
}
if (headingtype == HEAD_NONE) {
DebugF("Must have a heading before first line of data\n");
DebugF("Illegal line of data ignored\n");
continue;
}
len = strlen(line.line);
if ((tmp.name= strtok(line.line, " \t")) == NULL) {
DebugF("Huh? No token on line\n");
DebugF("Illegal line of data ignored\n");
continue;
}
if (strlen(tmp.name) == len) {
DebugF("No description found\n");
DebugF("Illegal line of data ignored\n");
continue;
}
tok = line.line + strlen(tmp.name) + 1;
while ((*tok!='\n')&&isspace(*tok))
tok++;
if (*tok == '\0') {
DebugF("No description found\n");
DebugF("Illegal line of data ignored\n");
continue;
}
tmp.desc= tok;
switch (headingtype) {
case HEAD_MODEL:
XkbRF_AddVarDescCopy(&rules->models,&tmp);
break;
case HEAD_LAYOUT:
XkbRF_AddVarDescCopy(&rules->layouts,&tmp);
break;
case HEAD_VARIANT:
XkbRF_AddVarDescCopy(&rules->variants,&tmp);
break;
case HEAD_OPTION:
XkbRF_AddVarDescCopy(&rules->options,&tmp);
break;
case HEAD_EXTRA:
XkbRF_AddVarDescCopy(&rules->extra[extra_ndx],&tmp);
break;
}
}
FreeInputLine(&line);
if ((rules->models.num_desc==0) && (rules->layouts.num_desc==0) &&
(rules->variants.num_desc==0) && (rules->options.num_desc==0) &&
(rules->num_extra==0)) {
return False;
}
return True;
}
Bool
XkbRF_LoadDescriptionsByName(char *base,char *locale,XkbRF_RulesPtr rules)
{
FILE * file;
char buf[PATH_MAX];
Bool ok;
if ((!base)||(!rules))
return False;
if (locale) {
if (strlen(base)+strlen(locale)+6 > PATH_MAX)
return False;
sprintf(buf,"%s-%s.lst", base, locale);
}
else {
if (strlen(base)+5 > PATH_MAX)
return False;
sprintf(buf,"%s.lst", base);
}
file= fopen(buf, "r");
if ((!file)&&(locale)) {
sprintf(buf,"%s.lst", base);
file= fopen(buf, "r");
}
if (!file)
return False;
ok= XkbRF_LoadDescriptions(file,rules);
fclose(file);
return ok;
}
XkbRF_RulesPtr
XkbRF_Load(char *base,char *locale,Bool wantDesc,Bool wantRules)
{
XkbRF_RulesPtr rules;
if ((!base)||((!wantDesc)&&(!wantRules)))
return NULL;
if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
return NULL;
if (wantDesc&&(!XkbRF_LoadDescriptionsByName(base,locale,rules))) {
XkbRF_Free(rules,True);
return NULL;
}
if (wantRules&&(!XkbRF_LoadRulesByName(base,locale,rules))) {
XkbRF_Free(rules,True);
return NULL;
}
return rules;
}
XkbRF_RulesPtr
XkbRF_Create(int szRules,int szExtra)
{
XkbRF_RulesPtr rules;
if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
return NULL;
if (szRules>0) {
rules->sz_rules= szRules;
rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
if (!rules->rules) {
_XkbFree(rules);
return NULL;
}
}
if (szExtra>0) {
rules->sz_extra= szExtra;
rules->extra= _XkbTypedCalloc(rules->sz_extra,XkbRF_DescribeVarsRec);
if (!rules->extra) {
if (rules->rules)
_XkbFree(rules->rules);
_XkbFree(rules);
return NULL;
}
}
return rules;
}
static void
XkbRF_ClearVarDescriptions(XkbRF_DescribeVarsPtr var)
{
register int i;
for (i=0;i<var->num_desc;i++) {
if (var->desc[i].name)
_XkbFree(var->desc[i].name);
if (var->desc[i].desc)
_XkbFree(var->desc[i].desc);
var->desc[i].name= var->desc[i].desc= NULL;
}
if (var->desc)
_XkbFree(var->desc);
var->desc= NULL;
return;
}
void
XkbRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
{
int i;
XkbRF_RulePtr rule;
XkbRF_GroupPtr group;
if (!rules)
return;
XkbRF_ClearVarDescriptions(&rules->models);
XkbRF_ClearVarDescriptions(&rules->layouts);
XkbRF_ClearVarDescriptions(&rules->variants);
XkbRF_ClearVarDescriptions(&rules->options);
if (rules->extra) {
for (i = 0; i < rules->num_extra; i++) {
XkbRF_ClearVarDescriptions(&rules->extra[i]);
}
_XkbFree(rules->extra);
rules->num_extra= rules->sz_extra= 0;
rules->extra= NULL;
}
if (rules->rules) {
for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
if (rule->model) _XkbFree(rule->model);
if (rule->layout) _XkbFree(rule->layout);
if (rule->variant) _XkbFree(rule->variant);
if (rule->option) _XkbFree(rule->option);
if (rule->keycodes) _XkbFree(rule->keycodes);
if (rule->symbols) _XkbFree(rule->symbols);
if (rule->types) _XkbFree(rule->types);
if (rule->compat) _XkbFree(rule->compat);
if (rule->geometry) _XkbFree(rule->geometry);
bzero((char *)rule,sizeof(XkbRF_RuleRec));
}
_XkbFree(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++) {
if (group->name) _XkbFree(group->name);
if (group->words) _XkbFree(group->words);
}
_XkbFree(rules->groups);
rules->num_groups= 0;
rules->groups= NULL;
}
if (freeRules)
_XkbFree(rules);
return;
}