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 {
PropValuePropValue37 PropValue() { lineno = -1; }
PropValuePropValue38 PropValue(const PropValue& that) { lineno=that.lineno; value=that.value; }
PropValuePropValue39 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
main(int argc,char ** argv)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
usage()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
is_whitespace(const char * p)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
parse_keycode(const char * filename,int lineno,char * str,int * value)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
parse_number(const char * filename,int lineno,char * str,int * value)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
parse_key_line(const char * filename,int lineno,char * line,KeyRecord * out)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
write_kr(int fd,const KeyRecord & kr)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