#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "keys.h"
#include "program.h"
#include "parser.h"
#include "dictionary.h"
#include "validkeys.h"
static int error=0;
static struct reserved reserved[]={
{"shift", SHIFT},
{"button", BUTTON},
{"axis", AXIS},
{"code", CODE},
{"script", SCRIPT},
{"include", INCLUDE},
{"joysticks", JOYSTICKS},
{"joystick", JOYSTICK},
{NULL, 0}
};
static char *known_keys[]={
"id",
"vendor",
"product",
"src",
"target",
"axis",
"plus",
"minus",
"device",
"flags",
"button",
"axes",
"buttons",
"min",
"max",
"deadzone",
"speed",
NULL
};
FILE *pfile=NULL;
FILE *fmap=NULL;
int line=1;
int cpos=1;
static int nextchar=NONE;
static dictionary dict=NULL;
static struct program_button_remap map;
static struct program_axis_remap amap;
static struct scriptmap script;
struct program_code program;
struct program_button_remap buttons[MAX_ASSIGN];
struct program_axis_remap axes[MAX_ASSIGN];
struct scriptmap scriptassign[MAX_ASSIGN];
static int parse_err=0;
int nbuttons=0;
int naxes=0;
int nscript=0;
int njoysticks=0;
struct joystick joysticks[8];
static int base=0;
static char *id, *vendor, *product, *src, *target, *button, *device, *flags, *axis, *plus, *minus, *min, *max, *deadzone, *speed;
static char compile[256]="";
static int ishex(char *s) {
char c;
if (*s!='0') return 0;
s++;
if (*s!='x') return 0;
s++;
while (*s!='\0') {
c=*s;
if ((('A'<=c)&&(c<='F'))||
(('a'<=c)&&(c<='f'))||
(('0'<=c)&&(c<='9'))) s++;
else return 0;
}
return 1;
}
static int isnum(char *s) {
char c;
while (*s!='\0') {
c=*s;
if (('0'<=c)&&(c<='9')) s++;
else return 0;
}
return 1;
}
int numeric(char *s) {
int r=0;
int sign = 1;
char msg[256];
if (s==NULL) return 0;
if (s[0] == '-') {
sign = -1;
s++;
}
if (ishex(s)) {
r=strtol(s, NULL, 16);
} else if (isnum(s)) {
r=strtol(s, NULL, 10);
} else {
sprintf(msg, "Expected a number, got %s instead", s);
report(msg);
return 0;
}
return sign * r;
}
static int get_device(char *s) {
char msg[256];
if (s==NULL) report("Device expected!");
if (strcmp(s, "joyaxis")==0) return DEVICE_JOYSTICK;
if (strcmp(s, "joybtn")==0) return DEVICE_JOYSTICK;
if (strcmp(s, "joystick")==0) return DEVICE_JOYSTICK;
if (strcmp(s, "kbd")==0) return DEVICE_KBD;
if (strcmp(s, "mouse")==0) return DEVICE_MOUSE;
sprintf(msg, "Expecting a device type:joyaxis,joybtn,joystick,kbd,mouse. Found %s instead", s);
report(msg);
return 255;
}
static int get_type(char *s, dictionary d) {
char *button, *axis, *plus, *minus;
if (s==NULL) return 255;
if (strcmp(s, "joyaxis")==0) return TYPE_AXIS;
if (strcmp(s, "joybtn")==0) return TYPE_BUTTON;
button=lookup_dictionary(d, "button");
axis=lookup_dictionary(d, "axis");
plus=lookup_dictionary(d, "plus");
minus=lookup_dictionary(d, "minus");
if ((button!=NULL)&&(axis!=NULL)) {
report("Only one of the keys 'button' and 'axis' may be specified");
return 255;
}
if ((plus!=NULL)&&(axis!=NULL)) {
report("Only one of the keys 'plus' and 'axis' may be specified");
return 255;
}
if ((minus!=NULL)&&(axis!=NULL)) {
report("Only one of the keys 'minus' and 'axis' may be specified");
return 255;
}
if (button!=NULL) return TYPE_BUTTON;
if (plus!=NULL) return TYPE_BUTTON;
if (minus!=NULL) return TYPE_BUTTON;
if (axis!=NULL) return TYPE_AXIS;
return 255;
}
static int parse_flags(char *s) {
int flags=FLAG_NONE;
char *p=s;
int more=1;
char msg[256];
if (s==NULL) return flags;
while (more) {
while ((*p!='\0')&&(*p!=',')&&(*p!=';')) p++;
if (*p=='\0') more=0;
*p='\0';
if (strcmp(s, "autorelease")==0) flags|=FLAG_AUTO_RELEASE;
else if (strcmp(s, "release")==0) flags|=FLAG_RELEASE;
else if (strcmp(s, "press")==0) flags|=FLAG_PRESS;
else if (strcmp(s, "shift")==0) flags|=FLAG_SHIFT;
else if (strcmp(s, "invert")==0) flags|=FLAG_INVERT;
else if (strcmp(s, "binary")==0) flags|=FLAG_BINARY;
else if (strcmp(s, "trinary")==0) flags|=FLAG_TRINARY;
else {
sprintf(msg, "Unknown flag %s", s);
report(msg);
}
s=p+1;
}
return flags;
}
static void parse_sequence(__uint16_t *sequence, char *s, int base, int type) {
char *p;
int releaseflag=0;
int value;
int i;
int n=0;
char msg[256];
if (s==NULL) {
sequence[0]=SEQUENCE_DONE;
return;
}
if (isnum(s)) {
if ((base==DEVICE_JOYSTICK)&&(type==TYPE_BUTTON)) {
base=BTN_JOYSTICK;
} else if ((base==DEVICE_MOUSE)&&(type==TYPE_BUTTON)) {
base=BTN_MOUSE;
} else base=0;
sequence[0]=numeric(s)+base;
sequence[1]=SEQUENCE_DONE;
} else {
p=s;
while (*p!='\0') {
//skip whitespace
while ((*p!='\0')&&(isspace(*p))) p++;
s=p;
while ((*p!='\0')&&(!(isspace(*p)))) p++;
if (*p!='\0') {
*p='\0';
p++;
}
value=-1;
if (strcmp(s, "REL")==0) releaseflag=RELEASEMASK;
else {
i=0;
while (keymap[i].value!=-1) {
if (strcmp(keymap[i].key, s)==0)
value=keymap[i].value;
i++;
}
if (value==-1) {
parse_err=1;
sprintf(msg, "Unknown key %s", s);
report(msg);
} else {
sequence[n]=value|releaseflag;
releaseflag=0;
n++;
}
}
s=p;
}
sequence[n]=SEQUENCE_DONE;
}
}
static void show_dictionary(dictionary dict) {
dictionary d=dict;
char *entry;
entry=get_current(d);
while (entry!=NULL) {
printf("%s ", entry);
d=next_entry(d);
entry=get_current(d);
}
}
static int has_required(dictionary dict, ...) {
va_list ap;
char *s;
char *entry;
char msg[256];
va_start(ap, dict);
s=va_arg(ap, char *);
while (s!=NULL) {
entry=lookup_dictionary(dict, s);
if (entry==NULL) {
sprintf(msg, "Missing key:%s", s);
report(msg);
printf("Defined keys: ");
show_dictionary(dict);
printf("\n");
va_end(ap);
return 0;
}
s=va_arg(ap, char *);
}
va_end(ap);
return 1;
}
void report(char *message) {
error=1;
fprintf(stderr, "%d:%d %s.\n", line, cpos, message);
}
void reportline(int line, int cpos, char *message) {
error=1;
fprintf(stderr, "%d:%d %s.\n", line, cpos, message);
}
int peekchar() {
if (nextchar!=NONE) return nextchar;
nextchar=fgetc(pfile);
return nextchar;
}
void eatchar() {
if (nextchar=='\n') {
line++;
cpos=1;
} else cpos++;
if (nextchar!=EOF)
nextchar=NONE;
}
int readchar() {
int c=peekchar();
eatchar();
return c;
}
static struct token newline() {
struct token t;
t.line=line;
t.pos=cpos;
t.type=NL;
strcpy(t.value, "\n");
eatchar();
return t;
}
static struct token eof() {
struct token t;
t.line=line;
t.pos=cpos;
t.type=EOF;
strcpy(t.value, "end of file");
return t;
}
static void parse_comment() {
//first character was #
//read until newline
int nc;
nc=peekchar();
while ((nc!='\n')&&(nc!=EOF)) {
eatchar();
nc=peekchar();
}
}
static void skipwhite() {
int nc=peekchar();
while (isspace(nc)&&(nc!='\n')) {
eatchar();
nc=peekchar();
}
}
static struct token parse_string() {
//opening quote has been read
//read until closing quote
struct token t;
int pos=0;
int nc;
t.line=line;
t.pos=cpos;
t.type=STRING;
nc=readchar();
nc=readchar();
while (nc!='"') {
if (nc==EOF) {
report("Unexpected end of file while parsing a string literal");
break;
}
t.value[pos++]=nc;
nc=readchar();
}
t.value[pos]='\0';
return t;
}
static struct token parse_value(struct token t, int pos) {
int nc;
t.type=VALUE;
if (pos>=0) {
nc=peekchar();
while (isalnum(nc)||(nc==',')||(nc==';')||(nc=='-')) {
t.value[pos++]=nc;
eatchar();
nc=peekchar();
}
t.value[pos]='\0';
}
nc=0;
while (reserved[nc].token!=NULL) {
if (strcmp(reserved[nc].token, t.value)==0) {
t.type=reserved[nc].value;
break;
}
nc++;
}
return t;
}
static struct token parse_id(struct token t, int pos) {
int nc;
t.type=ID;
nc=peekchar();
while (isalnum(nc)||(nc=='_')) {
t.value[pos++]=nc;
eatchar();
nc=peekchar();
}
t.value[pos]='\0';
nc=peekchar();
if (nc!='=') {
report("Expected an \"=\"");
} else eatchar();
return t;
}
static struct token parse_id_or_value() {
struct token t;
int pos=0;
int nc;
char message[256];
t.line=line;
t.pos=cpos;
nc=peekchar();
while (nc!=EOF) {
if (nc=='_') return parse_id(t, pos);
if (nc=='=') return parse_id(t, pos);
if (nc==',') return parse_value(t, pos);
if (nc==';') return parse_value(t, pos);
if (isspace(nc)) {
skipwhite();
nc=peekchar();
if (nc=='=') return parse_id(t, pos);
t.value[pos]='\0';
pos=-1;
return parse_value(t, pos);
}
if (isalnum(nc)) {
t.value[pos++]=nc;
} else {
sprintf(message, "Unexpected character \"%c\".", nc);
report(message);
eatchar();
break;
}
t.value[pos]='\0';
eatchar();
nc=peekchar();
}
t.type=ID;
report("Missing =");
return t;
}
static struct token maptoken() {
int skip=1;
int nc;
struct token t;
char message[256];
while (skip) {
nc=peekchar();
if (nc=='#')
parse_comment();
else if ((nc!='\n')&&isspace(nc))
skipwhite();
else skip=0;
}
if (nc=='\n') return newline();
if (nc==EOF) return eof();
if (nc=='"') return parse_string();
if (nc==',') return parse_value(t,0);
if (nc==';') return parse_value(t,0);
if (nc=='_') return parse_value(t,0);
if (nc=='-') return parse_value(t,0);
if (isdigit(nc)) return parse_value(t,0);
if (isalnum(nc)) return parse_id_or_value();
sprintf(message, "Unexpected character \"%c\".", nc);
report(message);
t.type=ERROR;
t.line=line;
t.pos=cpos;
t.value[0]=nc;
t.value[1]='\0';
eatchar();
return t;
}
struct token (*tokenizer)()=NULL;
static struct token nexttoken;
struct token peektoken() {
if (nexttoken.type!=NONE) return nexttoken;
nexttoken=tokenizer();
return nexttoken;
}
void eattoken() {
if (nexttoken.type!=EOF) nexttoken.type=NONE;
}
struct token readtoken() {
struct token t;
t=peektoken();
eattoken();
return t;
}
void init_tokenizer() {
nextchar=NONE;
nexttoken.type=NONE;
line=1;
cpos=1;
}
static int known_key(char *s) {
int i;
i=0;
while (known_keys[i]!=NULL) {
if (strcmp(known_keys[i], s)==0) return 1;
i++;
}
return 0;
}
static void parse_valuepairs() {
struct token key;
struct token value;
char message[300];
key=readtoken();
while ((key.type!=NL)||(key.type!=EOF)) {
if (key.type==NL) break;
if (key.type==EOF) break;
if (key.type==ID) {
if (!known_key(key.value)) {
sprintf(message, "Unknown key \"%s\"", key.value);
reportline(key.line, key.pos, message);
}
value=readtoken();
if ((value.type==STRING)||(value.type==VALUE)) {
dict=add_entry(dict, key.value, value.value);
} else {
sprintf(message, "Unexpected token \"%s\"", value.value);
reportline(value.line, value.pos, message);
}
} else {
sprintf(message, "Unexpected token \"%s\"", key.value);
reportline(key.line, key.pos, message);
}
key=readtoken();
}
}
static void parse_shift() {
struct token t;
t=readtoken();
parse_valuepairs();
//printf("shift ");
//show_dictionary(dict);
//printf("\n");
id=lookup_dictionary(dict, "id");
vendor=lookup_dictionary(dict, "vendor");
product=lookup_dictionary(dict, "product");
src=lookup_dictionary(dict, "src");
if ((id==NULL)&&((vendor==NULL)||(product==NULL))) {
reportline(t.line, t.pos, "Must have id, or vendor and product");
} else {
if (has_required(dict, "src", NULL)) {
map.program=PROGRAM_BUTTON_REMAP;
if (id!=NULL)
map.joystick=numeric(id);
else
map.joystick=255;
map.vendor=numeric(vendor);
map.product=numeric(product);
map.srcbutton=numeric(src);
map.type=TYPE_SHIFT;
map.flags=FLAG_NONE;
buttons[nbuttons]=map;
nbuttons++;
}
}
free_dictionary(dict);
dict=NULL;
}
static void parse_script() {
struct token t;
t=readtoken();
parse_valuepairs();
//printf("script ");
//show_dictionary(dict);
//printf("\n");
id=lookup_dictionary(dict, "id");
vendor=lookup_dictionary(dict, "vendor");
product=lookup_dictionary(dict, "product");
device=lookup_dictionary(dict, "device");
if ((id==NULL)&&((vendor==NULL)||(product==NULL))) {
reportline(t.line, t.pos, "Must have id, or vendor and product");
} else {
if (has_required(dict, "device", NULL)) {
if (id!=NULL)
script.id=numeric(id);
else
script.id=-1;
script.vendor=numeric(vendor);
script.product=numeric(product);
script.device=numeric(device);
scriptassign[nscript]=script;
nscript++;
}
}
free_dictionary(dict);
dict=NULL;
}
static void parse_button() {
struct token t;
int num;
t=readtoken();
parse_valuepairs();
id=lookup_dictionary(dict, "id");
vendor=lookup_dictionary(dict, "vendor");
product=lookup_dictionary(dict, "product");
src=lookup_dictionary(dict, "src");
target=lookup_dictionary(dict, "target");
button=lookup_dictionary(dict, "button");
axis=lookup_dictionary(dict, "axis");
device=lookup_dictionary(dict, "device");
flags=lookup_dictionary(dict, "flags");
speed=lookup_dictionary(dict, "speed");
if ((id==NULL)&&((vendor==NULL)||(product==NULL))) {
reportline(t.line, t.pos, "Must have id, or vendor and product");
} else {
if (has_required(dict, "src", "target", NULL)) {
map.program=PROGRAM_BUTTON_REMAP;
if (id!=NULL)
map.joystick=numeric(id);
else
map.joystick=255;
map.vendor=numeric(vendor);
map.product=numeric(product);
base=get_device(target);
map.device=base+(numeric(device)&0xF);
map.type=get_type(target,dict);
if (base==DEVICE_JOYSTICK) {
num=numeric(device);
if (num>8) {
report("Maximum of 8 joysticks allowed");
} else {
if (num>=njoysticks) {
njoysticks=num+1;
}
if (map.type==TYPE_AXIS) {
if (joysticks[num].axes<=numeric(axis))
joysticks[num].axes=numeric(axis)+1;
}
if (map.type==TYPE_BUTTON) {
if (joysticks[num].buttons<=numeric(button))
joysticks[num].buttons=numeric(button)+1;
}
}
}
map.srcbutton=numeric(src)+BTN_JOYSTICK;
map.speed=numeric(speed);
if (map.speed==0)
map.speed=8;
map.flags=parse_flags(flags);
if (button!=NULL)
parse_sequence(map.sequence, button, base, map.type);
else if (axis!=NULL)
parse_sequence(map.sequence, axis, base, map.type);
buttons[nbuttons]=map;
nbuttons++;
if (!((map.flags&FLAG_PRESS)||(map.flags&FLAG_RELEASE))&&
(map.sequence[1]==SEQUENCE_DONE)) {
if (map.sequence[0]&RELEASEMASK) {
map.sequence[0]&=~RELEASEMASK;
} else {
map.sequence[0]|=RELEASEMASK;
}
map.flags|=FLAG_RELEASE;
buttons[nbuttons]=map;
nbuttons++;
}
}
}
free_dictionary(dict);
dict=NULL;
}
static void parse_axis() {
struct token t;
int num;
t=readtoken();
parse_valuepairs();
//printf("axis ");
//show_dictionary(dict);
//printf("\n");
id=lookup_dictionary(dict, "id");
vendor=lookup_dictionary(dict, "vendor");
product=lookup_dictionary(dict, "product");
src=lookup_dictionary(dict, "src");
target=lookup_dictionary(dict, "target");
axis=lookup_dictionary(dict, "axis");
plus=lookup_dictionary(dict, "plus");
minus=lookup_dictionary(dict, "minus");
device=lookup_dictionary(dict, "device");
flags=lookup_dictionary(dict, "flags");
min=lookup_dictionary(dict, "min");
max=lookup_dictionary(dict, "max");
deadzone=lookup_dictionary(dict, "deadzone");
speed=lookup_dictionary(dict, "speed");
if ((id==NULL)&&((vendor==NULL)||(product==NULL))) {
reportline(t.line, t.pos, "Must have id, or vendor and product");
} else {
if (has_required(dict, "src", "target", NULL)) {
amap.program=PROGRAM_AXIS_REMAP;
if (id!=NULL)
amap.joystick=numeric(id);
else
amap.joystick=255;
amap.vendor=numeric(vendor);
amap.product=numeric(product);
amap.srcaxis=numeric(src);
base=get_device(target);
amap.device=base+(numeric(device)&0xF);
amap.type=get_type(target,dict);
amap.saved_value = 0;
if (base==DEVICE_JOYSTICK) {
num=numeric(device);
if (num>8) {
report("Maximum of 8 joysticks allowed");
} else {
if (num>=njoysticks) {
njoysticks=num+1;
}
if (amap.type==TYPE_AXIS) {
if (joysticks[num].axes<=numeric(axis))
joysticks[num].axes=numeric(axis)+1;
}
if (amap.type==TYPE_BUTTON) {
if (joysticks[num].buttons<=numeric(plus))
joysticks[num].buttons=numeric(plus)+1;
if (joysticks[num].buttons<=numeric(minus))
joysticks[num].buttons=numeric(minus)+1;
}
}
}
if ((base==DEVICE_JOYSTICK)&&(amap.type==TYPE_BUTTON)) {
base=BTN_JOYSTICK;
} else if ((base==DEVICE_MOUSE)&&(amap.type==TYPE_BUTTON)) {
base=BTN_MOUSE;
} else base=0;
parse_sequence(amap.plus, plus, base, amap.type);
parse_sequence(amap.minus, minus, base, amap.type);
amap.axis=numeric(axis);
amap.flags=parse_flags(flags);
amap.min=numeric(min);
amap.max=numeric(max);
amap.deadzone=numeric(deadzone);
amap.speed=numeric(speed);
if (amap.speed==0)
amap.speed=32767;
axes[naxes]=amap;
naxes++;
}
}
free_dictionary(dict);
dict=NULL;
}
static void parse_joystick() {
struct token t;
char *axes; char *buttons;
int num;
t=readtoken();
parse_valuepairs();
axes=lookup_dictionary(dict, "axes");
buttons=lookup_dictionary(dict, "buttons");
device=lookup_dictionary(dict, "device");
if (device==NULL) {
reportline(t.line, t.pos, "Must have device");
} else {
num=numeric(device);
if ((num<0)||(num>7)) reportline(t.line, t.pos, "Joystick must be 0-7");
//printf("joystick ");
//show_dictionary(dict);
//printf("\n");
if (num>=njoysticks) njoysticks=num+1;
if (axes!=NULL)
joysticks[num].axes=numeric(axes);
else
joysticks[num].axes=0;
if (buttons!=NULL)
joysticks[num].buttons=numeric(buttons);
else
joysticks[num].buttons=0;
}
free_dictionary(dict);
dict=NULL;
}
static void parse_code() {
struct token t;
t=readtoken();
t=readtoken();
//printf("code ");
if (t.type!=STRING) {
reportline(t.line, t.pos, "String expected");
} else strcpy(compile, t.value);
t=readtoken();
while ((t.type!=NL)&&(t.type!=EOF)) {
reportline(t.line, t.pos, "No further token expected on this line");
}
//printf("\"%s\"\n", compile);
}
static void parse_joysticks() {
struct token t;
int num;
t=readtoken();
t=readtoken();
printf("joysticks ");
if (t.type!=VALUE) {
reportline(t.line, t.pos, "Value expected");
} else num=numeric(t.value);
t=readtoken();
while ((t.type!=NL)&&(t.type!=EOF)) {
reportline(t.line, t.pos, "No further token expected on this line");
}
if (num<0) reportline(t.line, t.pos, "Only positive numbers allowed");
if (num>8) reportline(t.line, t.pos, "Maximum 8 joysticks allowed");
njoysticks=num;
printf("\"%d\"\n", num);
}
static void parse_lines();
static void parse_include() {
struct token t;
char include[256]="";
FILE *backup;
t=readtoken();
t=readtoken();
//printf("include ");
if (t.type!=STRING) {
reportline(t.line, t.pos, "String expected");
} else strcpy(include, t.value);
t=readtoken();
while ((t.type!=NL)&&(t.type!=EOF)) {
reportline(t.line, t.pos, "No further token expected on this line");
}
//printf("\"%s\"\n", include);
backup=pfile;
pfile=fopen(include, "r");
if (pfile==NULL) reportline(t.line, t.pos, "Could not find specified file");
else parse_lines();
pfile=backup;
}
static void parse_lines() {
struct token t;
char message[300];
t=peektoken();
while (t.type!=EOF) {
if (t.type==NL) {
t=readtoken();
//do nothing
} else if (t.type==EOF) {
t=readtoken();
//do nothing
} else if (t.type==CODE) {
parse_code();
} else if (t.type==BUTTON) {
parse_button();
} else if (t.type==AXIS) {
parse_axis();
} else if (t.type==SHIFT) {
parse_shift();
} else if (t.type==SCRIPT) {
parse_script();
} else if (t.type==JOYSTICK) {
parse_joystick();
} else if (t.type==JOYSTICKS) {
parse_joysticks();
} else if (t.type==INCLUDE) {
parse_include();
} else {
t=readtoken();
error=1;
if (t.type!=ERROR) {
sprintf(message, "Unexpected token \"%s\"", t.value);
reportline(t.line, t.pos, message);
while ((t.type!=EOF)&&(t.type!=NL)) {
t=readtoken();
}
}
}
t=peektoken();
}
}
int parse_map(void) {
pfile=fmap;
tokenizer=maptoken;
init_tokenizer();
parse_lines();
free_dictionary(dict);
dict=NULL;
if (fmap!=stdin) fclose(fmap);
if (strlen(compile)>0) {
fprintf(stderr, "parsing program %s\n", compile);
if (parse_program(compile, &program)!=0) error=1;
}
return error;
}