b7e96e4498
This patch adds read permission for everyone to the output files of the kcm tool. The compiled keycharmap files need to be able to be read by everyone. This fixes a problem at startup being unable to read the *.kcm.bin files located at <android-root>/system/usr/keychars.
421 lines
10 KiB
C++
421 lines
10 KiB
C++
#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, 0664);
|
|
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));
|
|
}
|
|
|