Home | History | Annotate | Download | only in kcm
      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <sys/types.h>
      4 #include <sys/stat.h>
      5 #include <fcntl.h>
      6 #include <unistd.h>
      7 #include <ui/KeycodeLabels.h>
      8 #include <stdlib.h>
      9 #include <ctype.h>
     10 #include <map>
     11 #include <string>
     12 #include <utils/ByteOrder.h>
     13 
     14 using namespace std;
     15 
     16 enum {
     17     LENDIAN,
     18     BENDIAN
     19 };
     20 
     21 /*
     22  * 1: KeyEvent name
     23  * 2: display_label
     24  * 3: number
     25  * 4..7: base, shift, alt, shift-alt
     26  */
     27 #define COLUMNS (3+4)
     28 
     29 struct KeyRecord
     30 {
     31     int lineno;
     32     int values[COLUMNS];
     33 };
     34 
     35 struct PropValue
     36 {
     37     PropValue() { lineno = -1; }
     38     PropValue(const PropValue& that) { lineno=that.lineno; value=that.value; }
     39     PropValue(int l, const string& v) { lineno = l; value = v; }
     40 
     41     int lineno;
     42     string value;
     43 };
     44 
     45 static int usage();
     46 
     47 //  0 -- ok
     48 // >0 -- error
     49 static int parse_key_line(const char* filename, int lineno, char* line,
     50         KeyRecord* out);
     51 static int write_kr(int fd, const KeyRecord& kr);
     52 
     53 int g_endian;
     54 
     55 int
     56 main(int argc, char** argv)
     57 {
     58     int err;
     59     if (argc != 3) {
     60         return usage();
     61     }
     62 
     63     const char* filename = argv[1];
     64     const char* outfilename = argv[2];
     65 
     66     int in = open(filename, O_RDONLY);
     67     if (in == -1) {
     68         fprintf(stderr, "kcm: error opening file for read: %s\n", filename);
     69         return 1;
     70     }
     71 
     72     off_t size = lseek(in, 0, SEEK_END);
     73     lseek(in, 0, SEEK_SET);
     74 
     75     char* input = (char*)malloc(size+1);
     76     read(in, input, size);
     77     input[size] = '\0';
     78 
     79     close(in);
     80     in = -1;
     81 
     82     map<string,PropValue> properties;
     83     map<int,KeyRecord> keys;
     84     int errorcount = 0;
     85     int lineno = 1;
     86     char *thisline = input;
     87     while (*thisline) {
     88         KeyRecord kr;
     89         char *nextline = thisline;
     90 
     91         while (*nextline != '\0' && *nextline != '\n' && *nextline != '\r') {
     92             nextline++;
     93         }
     94 
     95         // eat whitespace, but not newlines
     96         while (*thisline != '\0' && (*thisline == ' ' || *thisline == '\t')) {
     97             thisline++;
     98         }
     99 
    100         // find the end of the line
    101         char lineend = *nextline;
    102         *nextline = '\0';
    103         if (lineend == '\r' && nextline[1] == '\n') {
    104             nextline++;
    105         }
    106 
    107         if (*thisline == '\0' || *thisline == '\r' || *thisline == '\n'
    108                  || *thisline == '#') {
    109             // comment or blank line
    110         }
    111         else if (*thisline == '[') {
    112             // property - syntax [name=value]
    113             // look for =
    114             char* prop = thisline+1;
    115             char* end = prop;
    116             while (*end != '\0' && *end != '=') {
    117                 end++;
    118             }
    119             if (*end != '=') {
    120                 fprintf(stderr, "%s:%d: invalid property line: %s\n",
    121                         filename, lineno, thisline);
    122                 errorcount++;
    123             } else {
    124                 *end = '\0';
    125                 char* value = end+1;
    126                 end = nextline;
    127                 while (end > prop && *end != ']') {
    128                     end--;
    129                 }
    130                 if (*end != ']') {
    131                     fprintf(stderr, "%s:%d: property missing closing ]: %s\n",
    132                             filename, lineno, thisline);
    133                     errorcount++;
    134                 } else {
    135                     *end = '\0';
    136                     properties[prop] = PropValue(lineno, value);
    137                 }
    138             }
    139         }
    140         else {
    141             // key
    142             err = parse_key_line(filename, lineno, thisline, &kr);
    143             if (err == 0) {
    144                 kr.lineno = lineno;
    145 
    146                 map<int,KeyRecord>::iterator old = keys.find(kr.values[0]);
    147                 if (old != keys.end()) {
    148                     fprintf(stderr, "%s:%d: keycode %d already defined\n",
    149                             filename, lineno, kr.values[0]);
    150                     fprintf(stderr, "%s:%d: previously defined here\n",
    151                             filename, old->second.lineno);
    152                     errorcount++;
    153                 }
    154 
    155                 keys[kr.values[0]] = kr;
    156             }
    157             else if (err > 0) {
    158                 errorcount += err;
    159             }
    160         }
    161         lineno++;
    162 
    163         nextline++;
    164         thisline = nextline;
    165 
    166         if (errorcount > 20) {
    167             fprintf(stderr, "%s:%d: too many errors.  stopping.\n", filename,
    168                     lineno);
    169             return 1;
    170         }
    171     }
    172 
    173     free(input);
    174 
    175     map<string,PropValue>::iterator sit = properties.find("type");
    176     if (sit == properties.end()) {
    177         fprintf(stderr, "%s: key character map must contain type property.\n",
    178 		argv[0]);
    179         errorcount++;
    180     }
    181     PropValue pv = sit->second;
    182     unsigned char kbdtype = 0;
    183     if (pv.value == "NUMERIC") {
    184         kbdtype = 1;
    185     }
    186     else if (pv.value == "Q14") {
    187         kbdtype = 2;
    188     }
    189     else if (pv.value == "QWERTY") {
    190         kbdtype = 3;
    191     }
    192     else {
    193         fprintf(stderr, "%s:%d: keyboard type must be one of NUMERIC, Q14 "
    194                 " or QWERTY, not %s\n", filename, pv.lineno, pv.value.c_str());
    195     }
    196 
    197     if (errorcount != 0) {
    198         return 1;
    199     }
    200 
    201     int out = open(outfilename, O_RDWR|O_CREAT|O_TRUNC, 0664);
    202     if (out == -1) {
    203         fprintf(stderr, "kcm: error opening file for write: %s\n", outfilename);
    204         return 1;
    205     }
    206 
    207     int count = keys.size();
    208 
    209     map<int,KeyRecord>::iterator it;
    210     int n;
    211 
    212     /**
    213      * File Format:
    214      *    Offset    Description     Value
    215      *    0         magic string    "keychar"
    216      *    8         endian marker   0x12345678
    217      *    12        version         0x00000002
    218      *    16        key count       number of key entries
    219      *    20        keyboard type   NUMERIC, Q14, QWERTY, etc.
    220      *    21        padding         0
    221      *    32        the keys
    222      */
    223     err = write(out, "keychar", 8);
    224     if (err == -1) goto bad_write;
    225 
    226     n = htodl(0x12345678);
    227     err = write(out, &n, 4);
    228     if (err == -1) goto bad_write;
    229 
    230     n = htodl(0x00000002);
    231     err = write(out, &n, 4);
    232     if (err == -1) goto bad_write;
    233 
    234     n = htodl(count);
    235     err = write(out, &n, 4);
    236     if (err == -1) goto bad_write;
    237 
    238     err = write(out, &kbdtype, 1);
    239     if (err == -1) goto bad_write;
    240 
    241     char zero[11];
    242     memset(zero, 0, 11);
    243     err = write(out, zero, 11);
    244     if (err == -1) goto bad_write;
    245 
    246     for (it = keys.begin(); it != keys.end(); it++) {
    247         const KeyRecord& kr = it->second;
    248         /*
    249         printf("%2d/ [%d] [%d] [%d] [%d] [%d] [%d] [%d]\n", kr.lineno,
    250                 kr.values[0], kr.values[1], kr.values[2], kr.values[3],
    251                 kr.values[4], kr.values[5], kr.values[6]);
    252         */
    253         err = write_kr(out, kr);
    254         if (err == -1) goto bad_write;
    255     }
    256 
    257     close(out);
    258     return 0;
    259 
    260 bad_write:
    261     fprintf(stderr, "kcm: fatal error writing to file: %s\n", outfilename);
    262     close(out);
    263     unlink(outfilename);
    264     return 1;
    265 }
    266 
    267 static int usage()
    268 {
    269     fprintf(stderr,
    270             "usage: kcm INPUT OUTPUT\n"
    271             "\n"
    272             "INPUT   keycharmap file\n"
    273             "OUTPUT  compiled keycharmap file\n"
    274         );
    275     return 1;
    276 }
    277 
    278 static int
    279 is_whitespace(const char* p)
    280 {
    281     while (*p) {
    282         if (!isspace(*p)) {
    283             return 0;
    284         }
    285         p++;
    286     }
    287     return 1;
    288 }
    289 
    290 
    291 static int
    292 parse_keycode(const char* filename, int lineno, char* str, int* value)
    293 {
    294     const KeycodeLabel *list = KEYCODES;
    295     while (list->literal) {
    296         if (0 == strcmp(str, list->literal)) {
    297             *value = list->value;
    298             return 0;
    299         }
    300         list++;
    301     }
    302 
    303     char* endptr;
    304     *value = strtol(str, &endptr, 0);
    305     if (*endptr != '\0') {
    306         fprintf(stderr, "%s:%d: expected keycode label or number near: "
    307                 "%s\n", filename, lineno, str);
    308         return 1;
    309     }
    310 
    311     if (*value == 0) {
    312         fprintf(stderr, "%s:%d: 0 is not a valid keycode.\n",
    313                 filename, lineno);
    314         return 1;
    315     }
    316 
    317     return 0;
    318 }
    319 
    320 static int
    321 parse_number(const char* filename, int lineno, char* str, int* value)
    322 {
    323     int len = strlen(str);
    324 
    325     if (len == 3 && str[0] == '\'' && str[2] == '\'') {
    326         if (str[1] > 0 && str[1] < 127) {
    327             *value = (int)str[1];
    328             return 0;
    329         } else {
    330             fprintf(stderr, "%s:%d: only low ascii characters are allowed in"
    331                     " quotes near: %s\n", filename, lineno, str);
    332             return 1;
    333         }
    334     }
    335 
    336     char* endptr;
    337     *value = strtol(str, &endptr, 0);
    338     if (*endptr != '\0') {
    339         fprintf(stderr, "%s:%d: expected number or quoted ascii but got: %s\n",
    340                 filename, lineno, str);
    341         return 1;
    342     }
    343 
    344     if (*value >= 0xfffe || *value < 0) {
    345         fprintf(stderr, "%s:%d: unicode char out of range (no negatives, "
    346                 "nothing larger than 0xfffe): %s\n", filename, lineno, str);
    347         return 1;
    348     }
    349 
    350     return 0;
    351 }
    352 
    353 static int
    354 parse_key_line(const char* filename, int lineno, char* line, KeyRecord* out)
    355 {
    356     char* p = line;
    357 
    358     int len = strlen(line);
    359     char* s[COLUMNS];
    360     for (int i=0; i<COLUMNS; i++) {
    361         s[i] = (char*)malloc(len+1);
    362     }
    363 
    364     for (int i = 0; i < COLUMNS; i++) {
    365         while (*p != '\0' && isspace(*p)) {
    366             p++;
    367         }
    368 
    369         if (*p == '\0') {
    370             fprintf(stderr, "%s:%d: not enough on this line: %s\n", filename,
    371                     lineno, line);
    372             return 1;
    373         }
    374 
    375         char *p1 = p;
    376         while (*p != '\0' && !isspace(*p)) {
    377             p++;
    378         }
    379 
    380         memcpy(s[i], p1, p - p1);
    381         s[i][p - p1] = '\0';
    382     }
    383 
    384     while (*p != '\0' && isspace(*p)) {
    385         *p++;
    386     }
    387     if (*p != '\0') {
    388         fprintf(stderr, "%s:%d: too much on one line near: %s\n", filename,
    389                 lineno, p);
    390         fprintf(stderr, "%s:%d: -->%s<--\n", filename, lineno, line);
    391         return 1;
    392     }
    393 
    394     int errorcount = parse_keycode(filename, lineno, s[0], &out->values[0]);
    395     for (int i=1; i<COLUMNS && errorcount == 0; i++) {
    396         errorcount += parse_number(filename, lineno, s[i], &out->values[i]);
    397     }
    398 
    399     return errorcount;
    400 }
    401 
    402 struct WrittenRecord
    403 {
    404     unsigned int keycode;       // 4 bytes
    405     unsigned short values[COLUMNS - 1];   // 6*2 bytes = 12
    406                                 // 16 bytes total
    407 };
    408 
    409 static int
    410 write_kr(int fd, const KeyRecord& kr)
    411 {
    412     WrittenRecord wr;
    413 
    414     wr.keycode = htodl(kr.values[0]);
    415     for (int i=0; i<COLUMNS - 1; i++) {
    416         wr.values[i] = htods(kr.values[i+1]);
    417     }
    418 
    419     return write(fd, &wr, sizeof(WrittenRecord));
    420 }
    421 
    422