#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ui/KeycodeLabels.h>
#include <stdlib.h>
#include <ctype.h>
#include <map>
#include <string>
#include <utils/ByteOrder.h>
using namespace std;
enum {
LENDIAN,
BENDIAN
};
/*
* 1: KeyEvent name
* 2: display_label
* 3: number
* 4..7: base, shift, alt, shift-alt
*/
#define COLUMNS (3+4)
struct KeyRecord
{
int lineno;
int values[COLUMNS];
};
struct PropValue
{
PropValue() { lineno = -1; }
PropValue(const PropValue& that) { lineno=that.lineno; value=that.value; }
PropValue(int l, const string& v) { lineno = l; value = v; }
int lineno;
string value;
};
static int usage();
// 0 -- ok
// >0 -- error
static int parse_key_line(const char* filename, int lineno, char* line,
KeyRecord* out);
static int write_kr(int fd, const KeyRecord& kr);
int g_endian;
int
main(int argc, char** argv)
{
int err;
if (argc != 3) {
return usage();
}
const char* filename = argv[1];
const char* outfilename = argv[2];
int in = open(filename, O_RDONLY);
if (in == -1) {
fprintf(stderr, "kcm: error opening file for read: %s\n", filename);
return 1;
}
off_t size = lseek(in, 0, SEEK_END);
lseek(in, 0, SEEK_SET);
char* input = (char*)malloc(size+1);
read(in, input, size);
input[size] = '\0';
close(in);
in = -1;
map<string,PropValue> properties;
map<int,KeyRecord> keys;
int errorcount = 0;
int lineno = 1;
char *thisline = input;
while (*thisline) {
KeyRecord kr;
char *nextline = thisline;
while (*nextline != '\0' && *nextline != '\n' && *nextline != '\r') {
nextline++;
}
// eat whitespace, but not newlines
while (*thisline != '\0' && (*thisline == ' ' || *thisline == '\t')) {
thisline++;
}
// find the end of the line
char lineend = *nextline;
*nextline = '\0';
if (lineend == '\r' && nextline[1] == '\n') {
nextline++;
}
if (*thisline == '\0' || *thisline == '\r' || *thisline == '\n'
|| *thisline == '#') {
// comment or blank line
}
else if (*thisline == '[') {
// property - syntax [name=value]
// look for =
char* prop = thisline+1;
char* end = prop;
while (*end != '\0' && *end != '=') {
end++;
}
if (*end != '=') {
fprintf(stderr, "%s:%d: invalid property line: %s\n",
filename, lineno, thisline);
errorcount++;
} else {
*end = '\0';
char* value = end+1;
end = nextline;
while (end > prop && *end != ']') {
end--;
}
if (*end != ']') {
fprintf(stderr, "%s:%d: property missing closing ]: %s\n",
filename, lineno, thisline);
errorcount++;
} else {
*end = '\0';
properties[prop] = PropValue(lineno, value);
}
}
}
else {
// key
err = parse_key_line(filename, lineno, thisline, &kr);
if (err == 0) {
kr.lineno = lineno;
map<int,KeyRecord>::iterator old = keys.find(kr.values[0]);
if (old != keys.end()) {
fprintf(stderr, "%s:%d: keycode %d already defined\n",
filename, lineno, kr.values[0]);
fprintf(stderr, "%s:%d: previously defined here\n",
filename, old->second.lineno);
errorcount++;
}
keys[kr.values[0]] = kr;
}
else if (err > 0) {
errorcount += err;
}
}
lineno++;
nextline++;
thisline = nextline;
if (errorcount > 20) {
fprintf(stderr, "%s:%d: too many errors. stopping.\n", filename,
lineno);
return 1;
}
}
free(input);
map<string,PropValue>::iterator sit = properties.find("type");
if (sit == properties.end()) {
fprintf(stderr, "%s: key character map must contain type property.\n",
argv[0]);
errorcount++;
}
PropValue pv = sit->second;
unsigned char kbdtype = 0;
if (pv.value == "NUMERIC") {
kbdtype = 1;
}
else if (pv.value == "Q14") {
kbdtype = 2;
}
else if (pv.value == "QWERTY") {
kbdtype = 3;
}
else {
fprintf(stderr, "%s:%d: keyboard type must be one of NUMERIC, Q14 "
" or QWERTY, not %s\n", filename, pv.lineno, pv.value.c_str());
}
if (errorcount != 0) {
return 1;
}
int out = open(outfilename, O_RDWR|O_CREAT|O_TRUNC, 0660);
if (out == -1) {
fprintf(stderr, "kcm: error opening file for write: %s\n", outfilename);
return 1;
}
int count = keys.size();
map<int,KeyRecord>::iterator it;
int n;
/**
* File Format:
* Offset Description Value
* 0 magic string "keychar"
* 8 endian marker 0x12345678
* 12 version 0x00000002
* 16 key count number of key entries
* 20 keyboard type NUMERIC, Q14, QWERTY, etc.
* 21 padding 0
* 32 the keys
*/
err = write(out, "keychar", 8);
if (err == -1) goto bad_write;
n = htodl(0x12345678);
err = write(out, &n, 4);
if (err == -1) goto bad_write;
n = htodl(0x00000002);
err = write(out, &n, 4);
if (err == -1) goto bad_write;
n = htodl(count);
err = write(out, &n, 4);
if (err == -1) goto bad_write;
err = write(out, &kbdtype, 1);
if (err == -1) goto bad_write;
char zero[11];
memset(zero, 0, 11);
err = write(out, zero, 11);
if (err == -1) goto bad_write;
for (it = keys.begin(); it != keys.end(); it++) {
const KeyRecord& kr = it->second;
/*
printf("%2d/ [%d] [%d] [%d] [%d] [%d] [%d] [%d]\n", kr.lineno,
kr.values[0], kr.values[1], kr.values[2], kr.values[3],
kr.values[4], kr.values[5], kr.values[6]);
*/
err = write_kr(out, kr);
if (err == -1) goto bad_write;
}
close(out);
return 0;
bad_write:
fprintf(stderr, "kcm: fatal error writing to file: %s\n", outfilename);
close(out);
unlink(outfilename);
return 1;
}
static int usage()
{
fprintf(stderr,
"usage: kcm INPUT OUTPUT\n"
"\n"
"INPUT keycharmap file\n"
"OUTPUT compiled keycharmap file\n"
);
return 1;
}
static int
is_whitespace(const char* p)
{
while (*p) {
if (!isspace(*p)) {
return 0;
}
p++;
}
return 1;
}
static int
parse_keycode(const char* filename, int lineno, char* str, int* value)
{
const KeycodeLabel *list = KEYCODES;
while (list->literal) {
if (0 == strcmp(str, list->literal)) {
*value = list->value;
return 0;
}
list++;
}
char* endptr;
*value = strtol(str, &endptr, 0);
if (*endptr != '\0') {
fprintf(stderr, "%s:%d: expected keycode label or number near: "
"%s\n", filename, lineno, str);
return 1;
}
if (*value == 0) {
fprintf(stderr, "%s:%d: 0 is not a valid keycode.\n",
filename, lineno);
return 1;
}
return 0;
}
static int
parse_number(const char* filename, int lineno, char* str, int* value)
{
int len = strlen(str);
if (len == 3 && str[0] == '\'' && str[2] == '\'') {
if (str[1] > 0 && str[1] < 127) {
*value = (int)str[1];
return 0;
} else {
fprintf(stderr, "%s:%d: only low ascii characters are allowed in"
" quotes near: %s\n", filename, lineno, str);
return 1;
}
}
char* endptr;
*value = strtol(str, &endptr, 0);
if (*endptr != '\0') {
fprintf(stderr, "%s:%d: expected number or quoted ascii but got: %s\n",
filename, lineno, str);
return 1;
}
if (*value >= 0xfffe || *value < 0) {
fprintf(stderr, "%s:%d: unicode char out of range (no negatives, "
"nothing larger than 0xfffe): %s\n", filename, lineno, str);
return 1;
}
return 0;
}
static int
parse_key_line(const char* filename, int lineno, char* line, KeyRecord* out)
{
char* p = line;
int len = strlen(line);
char* s[COLUMNS];
for (int i=0; i<COLUMNS; i++) {
s[i] = (char*)malloc(len+1);
}
for (int i = 0; i < COLUMNS; i++) {
while (*p != '\0' && isspace(*p)) {
p++;
}
if (*p == '\0') {
fprintf(stderr, "%s:%d: not enough on this line: %s\n", filename,
lineno, line);
return 1;
}
char *p1 = p;
while (*p != '\0' && !isspace(*p)) {
p++;
}
memcpy(s[i], p1, p - p1);
s[i][p - p1] = '\0';
}
while (*p != '\0' && isspace(*p)) {
*p++;
}
if (*p != '\0') {
fprintf(stderr, "%s:%d: too much on one line near: %s\n", filename,
lineno, p);
fprintf(stderr, "%s:%d: -->%s<--\n", filename, lineno, line);
return 1;
}
int errorcount = parse_keycode(filename, lineno, s[0], &out->values[0]);
for (int i=1; i<COLUMNS && errorcount == 0; i++) {
errorcount += parse_number(filename, lineno, s[i], &out->values[i]);
}
return errorcount;
}
struct WrittenRecord
{
unsigned int keycode; // 4 bytes
unsigned short values[COLUMNS - 1]; // 6*2 bytes = 12
// 16 bytes total
};
static int
write_kr(int fd, const KeyRecord& kr)
{
WrittenRecord wr;
wr.keycode = htodl(kr.values[0]);
for (int i=0; i<COLUMNS - 1; i++) {
wr.values[i] = htods(kr.values[i+1]);
}
return write(fd, &wr, sizeof(WrittenRecord));
}