#include #include #include #include #include #include #include #include #include #include #include #include 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 properties; map 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::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::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::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%s<--\n", filename, lineno, line); return 1; } int errorcount = parse_keycode(filename, lineno, s[0], &out->values[0]); for (int i=1; ivalues[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