xref: /haiku/src/bin/keymap/Keymap.cpp (revision 3be9edf8da228afd9fec0390f408c964766122aa)
1 /*
2  * Copyright 2004-2009, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jérôme Duval
7  *		Axel Dörfler, axeld@pinc-software.de.
8  */
9 
10 
11 #include "Keymap.h"
12 
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include <ByteOrder.h>
18 #include <Entry.h>
19 #include <File.h>
20 #include <FindDirectory.h>
21 #include <Path.h>
22 #include <String.h>
23 
24 
25 #define CHARS_TABLE_MAXSIZE  10000
26 
27 
28 extern status_t _restore_key_map_();
29 
30 
31 // i couldn't put patterns and pattern bufs on the stack without segfaulting
32 // regexp patterns
33 const char versionPattern[]
34 	= "Version[[:space:]]+=[[:space:]]+\\([[:digit:]]+\\)";
35 const char capslockPattern[]
36 	= "CapsLock[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
37 const char scrolllockPattern[]
38 	= "ScrollLock[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
39 const char numlockPattern[]
40 	= "NumLock[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
41 const char lshiftPattern[] = "LShift[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
42 const char rshiftPattern[] = "RShift[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
43 const char lcommandPattern[]
44 	= "LCommand[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
45 const char rcommandPattern[]
46 	= "RCommand[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
47 const char lcontrolPattern[]
48 	= "LControl[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
49 const char rcontrolPattern[]
50 	= "RControl[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
51 const char loptionPattern[]
52 	= "LOption[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
53 const char roptionPattern[]
54 	= "ROption[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
55 const char menuPattern[] = "Menu[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
56 const char locksettingsPattern[]
57 	= "LockSettings[[:space:]]+=[[:space:]]+\\([[:alnum:]]*\\)"
58 		"[[:space:]]*\\([[:alnum:]]*\\)"
59 		"[[:space:]]*\\([[:alnum:]]*\\)[[:space:]]*";
60 const char keyPattern[] = "Key[[:space:]]+\\([[:alnum:]]+\\)[[:space:]]+="
61 	"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
62 	"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
63 	"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
64 	"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
65 	"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
66 	"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
67 	"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
68 	"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
69 	"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
70 	"[[:space:]]+";
71 
72 
73 const char acutePattern[] = "Acute[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
74 	"[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
75 const char gravePattern[] = "Grave[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
76 	"[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
77 const char circumflexPattern[] = "Circumflex[[:space:]]+\\([[:alnum:]]"
78 	"+\\|'.*'\\)[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
79 const char diaeresisPattern[] = "Diaeresis[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
80 	"[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
81 const char tildePattern[] = "Tilde[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
82 	"[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
83 const char acutetabPattern[] = "AcuteTab[[:space:]]+="
84 	"[[:space:]]+\\([[:alnum:]-]*\\)"
85 	"[[:space:]]*\\([[:alnum:]-]*\\)"
86 	"[[:space:]]*\\([[:alnum:]-]*\\)"
87 	"[[:space:]]*\\([[:alnum:]-]*\\)"
88 	"[[:space:]]*\\([[:alnum:]-]*\\)"
89 	"[[:space:]]*\\([[:alnum:]-]*\\)"
90 	"[[:space:]]*\\([[:alnum:]-]*\\)"
91 	"[[:space:]]*\\([[:alnum:]-]*\\)"
92 	"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
93 const char gravetabPattern[] = "GraveTab[[:space:]]+="
94 	"[[:space:]]+\\([[:alnum:]-]*\\)"
95 	"[[:space:]]*\\([[:alnum:]-]*\\)"
96 	"[[:space:]]*\\([[:alnum:]-]*\\)"
97 	"[[:space:]]*\\([[:alnum:]-]*\\)"
98 	"[[:space:]]*\\([[:alnum:]-]*\\)"
99 	"[[:space:]]*\\([[:alnum:]-]*\\)"
100 	"[[:space:]]*\\([[:alnum:]-]*\\)"
101 	"[[:space:]]*\\([[:alnum:]-]*\\)"
102 	"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
103 const char circumflextabPattern[] = "CircumflexTab[[:space:]]+="
104 	"[[:space:]]+\\([[:alnum:]-]*\\)"
105 	"[[:space:]]*\\([[:alnum:]-]*\\)"
106 	"[[:space:]]*\\([[:alnum:]-]*\\)"
107 	"[[:space:]]*\\([[:alnum:]-]*\\)"
108 	"[[:space:]]*\\([[:alnum:]-]*\\)"
109 	"[[:space:]]*\\([[:alnum:]-]*\\)"
110 	"[[:space:]]*\\([[:alnum:]-]*\\)"
111 	"[[:space:]]*\\([[:alnum:]-]*\\)"
112 	"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
113 const char diaeresistabPattern[] = "DiaeresisTab[[:space:]]+="
114 	"[[:space:]]+\\([[:alnum:]-]*\\)"
115 	"[[:space:]]*\\([[:alnum:]-]*\\)"
116 	"[[:space:]]*\\([[:alnum:]-]*\\)"
117 	"[[:space:]]*\\([[:alnum:]-]*\\)"
118 	"[[:space:]]*\\([[:alnum:]-]*\\)"
119 	"[[:space:]]*\\([[:alnum:]-]*\\)"
120 	"[[:space:]]*\\([[:alnum:]-]*\\)"
121 	"[[:space:]]*\\([[:alnum:]-]*\\)"
122 	"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
123 const char tildetabPattern[] = "TildeTab[[:space:]]+="
124 	"[[:space:]]+\\([[:alnum:]-]*\\)"
125 	"[[:space:]]*\\([[:alnum:]-]*\\)"
126 	"[[:space:]]*\\([[:alnum:]-]*\\)"
127 	"[[:space:]]*\\([[:alnum:]-]*\\)"
128 	"[[:space:]]*\\([[:alnum:]-]*\\)"
129 	"[[:space:]]*\\([[:alnum:]-]*\\)"
130 	"[[:space:]]*\\([[:alnum:]-]*\\)"
131 	"[[:space:]]*\\([[:alnum:]-]*\\)"
132 	"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
133 
134 
135 // re_pattern_buffer buffers
136 struct re_pattern_buffer versionBuf;
137 struct re_pattern_buffer capslockBuf;
138 struct re_pattern_buffer scrolllockBuf;
139 struct re_pattern_buffer numlockBuf;
140 struct re_pattern_buffer lshiftBuf;
141 struct re_pattern_buffer rshiftBuf;
142 struct re_pattern_buffer lcommandBuf;
143 struct re_pattern_buffer rcommandBuf;
144 struct re_pattern_buffer lcontrolBuf;
145 struct re_pattern_buffer rcontrolBuf;
146 struct re_pattern_buffer loptionBuf;
147 struct re_pattern_buffer roptionBuf;
148 struct re_pattern_buffer menuBuf;
149 struct re_pattern_buffer locksettingsBuf;
150 struct re_pattern_buffer keyBuf;
151 struct re_pattern_buffer acuteBuf;
152 struct re_pattern_buffer graveBuf;
153 struct re_pattern_buffer circumflexBuf;
154 struct re_pattern_buffer diaeresisBuf;
155 struct re_pattern_buffer tildeBuf;
156 struct re_pattern_buffer acutetabBuf;
157 struct re_pattern_buffer gravetabBuf;
158 struct re_pattern_buffer circumflextabBuf;
159 struct re_pattern_buffer diaeresistabBuf;
160 struct re_pattern_buffer tildetabBuf;
161 
162 
163 void
164 dump_map(FILE* file, const char* name, int32* map)
165 {
166 	fprintf(file, "\t%s:{\n", name);
167 
168 	for (uint32 i = 0; i < 16; i++) {
169 		fprintf(file, "\t\t");
170 		for (uint32 j = 0; j < 8; j++) {
171 			fprintf(file, "0x%04" B_PRIx32 ",%s", map[i * 8 + j],
172 				j < 7 ? " " : "");
173 		}
174 		fprintf(file, "\n");
175 	}
176 	fprintf(file, "\t},\n");
177 }
178 
179 
180 void
181 dump_keys(FILE* file, const char* name, int32* keys)
182 {
183 	fprintf(file, "\t%s:{\n", name);
184 
185 	for (uint32 i = 0; i < 4; i++) {
186 		fprintf(file, "\t\t");
187 		for (uint32 j = 0; j < 8; j++) {
188 			fprintf(file, "0x%04" B_PRIx32 ",%s", keys[i * 8 + j],
189 				j < 7 ? " " : "");
190 		}
191 		fprintf(file, "\n");
192 	}
193 	fprintf(file, "\t},\n");
194 }
195 
196 
197 //	#pragma mark -
198 
199 
200 Keymap::Keymap()
201 	:
202 	fChars(NULL),
203 	fCharsSize(0)
204 {
205 	memset(&fKeys, 0, sizeof(fKeys));
206 }
207 
208 
209 Keymap::~Keymap()
210 {
211 	delete[] fChars;
212 }
213 
214 
215 status_t
216 Keymap::LoadCurrent()
217 {
218 #if (defined(__BEOS__) || defined(__HAIKU__))
219 	key_map* keys = NULL;
220 	get_key_map(&keys, &fChars);
221 	if (!keys)
222 		return B_ERROR;
223 
224 	memcpy(&fKeys, keys, sizeof(fKeys));
225 	free(keys);
226 	return B_OK;
227 #else	// ! __BEOS__
228 	fprintf(stderr, "Unsupported operation on this platform!\n");
229 	exit(1);
230 #endif	// ! __BEOS__
231 }
232 
233 
234 /*!	Load a map from a file.
235 
236 	file format in big endian:
237 		struct key_map
238 		uint32 size of following charset
239 		charset (offsets go into this with size of character followed by
240 		  character)
241 */
242 status_t
243 Keymap::Load(const char* name)
244 {
245 	FILE* file = fopen(name, "r");
246 	if (file == NULL)
247 		return errno;
248 
249 	return Load(file);
250 }
251 
252 
253 status_t
254 Keymap::Load(FILE* file)
255 {
256 	if (fread(&fKeys, sizeof(fKeys), 1, file) < 1)
257 		return B_BAD_VALUE;
258 
259 	// convert from big-endian
260 	for (uint32 i = 0; i < sizeof(fKeys) / 4; i++) {
261 		((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
262 	}
263 
264 	if (fKeys.version != 3)
265 		return KEYMAP_ERROR_UNKNOWN_VERSION;
266 
267 	if (fread(&fCharsSize, sizeof(uint32), 1, file) < 1)
268 		return B_BAD_VALUE;
269 
270 	fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
271 	if (fCharsSize > 16 * 1024)
272 		return B_BAD_DATA;
273 
274 	delete[] fChars;
275 	fChars = new char[fCharsSize];
276 
277 	if (fread(fChars, 1, fCharsSize, file) != fCharsSize)
278 		return B_BAD_VALUE;
279 
280 	return B_OK;
281 }
282 
283 
284 status_t
285 Keymap::LoadSource(const char* name)
286 {
287 	FILE* file = fopen(name, "r");
288 	if (file == NULL)
289 		return errno;
290 
291 	status_t status = LoadSource(file);
292 	fclose(file);
293 
294 	return status;
295 }
296 
297 
298 status_t
299 Keymap::LoadSource(FILE* file)
300 {
301 	// Setup regexp parser
302 
303 	reg_syntax_t syntax = RE_CHAR_CLASSES;
304 	re_set_syntax(syntax);
305 
306 	const char* error = re_compile_pattern(versionPattern,
307 		strlen(versionPattern), &versionBuf);
308 	if (error)
309 		fprintf(stderr, error);
310 	error = re_compile_pattern(capslockPattern, strlen(capslockPattern),
311 		&capslockBuf);
312 	if (error)
313 		fprintf(stderr, error);
314 	error = re_compile_pattern(scrolllockPattern, strlen(scrolllockPattern),
315 		&scrolllockBuf);
316 	if (error)
317 		fprintf(stderr, error);
318 	error = re_compile_pattern(numlockPattern, strlen(numlockPattern),
319 		&numlockBuf);
320 	if (error)
321 		fprintf(stderr, error);
322 	error = re_compile_pattern(lshiftPattern, strlen(lshiftPattern), &lshiftBuf);
323 	if (error)
324 		fprintf(stderr, error);
325 	error = re_compile_pattern(rshiftPattern, strlen(rshiftPattern), &rshiftBuf);
326 	if (error)
327 		fprintf(stderr, error);
328 	error = re_compile_pattern(lcommandPattern, strlen(lcommandPattern),
329 		&lcommandBuf);
330 	if (error)
331 		fprintf(stderr, error);
332 	error = re_compile_pattern(rcommandPattern, strlen(rcommandPattern),
333 		&rcommandBuf);
334 	if (error)
335 		fprintf(stderr, error);
336 	error = re_compile_pattern(lcontrolPattern, strlen(lcontrolPattern),
337 		&lcontrolBuf);
338 	if (error)
339 		fprintf(stderr, error);
340 	error = re_compile_pattern(rcontrolPattern, strlen(rcontrolPattern),
341 		&rcontrolBuf);
342 	if (error)
343 		fprintf(stderr, error);
344 	error = re_compile_pattern(loptionPattern, strlen(loptionPattern),
345 		&loptionBuf);
346 	if (error)
347 		fprintf(stderr, error);
348 	error = re_compile_pattern(roptionPattern, strlen(roptionPattern),
349 		&roptionBuf);
350 	if (error)
351 		fprintf(stderr, error);
352 	error = re_compile_pattern(menuPattern, strlen(menuPattern), &menuBuf);
353 	if (error)
354 		fprintf(stderr, error);
355 	error = re_compile_pattern(locksettingsPattern, strlen(locksettingsPattern),
356 		&locksettingsBuf);
357 	if (error)
358 		fprintf(stderr, error);
359 	error = re_compile_pattern(keyPattern, strlen(keyPattern), &keyBuf);
360 	if (error)
361 		fprintf(stderr, error);
362 	error = re_compile_pattern(acutePattern, strlen(acutePattern), &acuteBuf);
363 	if (error)
364 		fprintf(stderr, error);
365 	error = re_compile_pattern(gravePattern, strlen(gravePattern), &graveBuf);
366 	if (error)
367 		fprintf(stderr, error);
368 	error = re_compile_pattern(circumflexPattern, strlen(circumflexPattern),
369 		&circumflexBuf);
370 	if (error)
371 		fprintf(stderr, error);
372 	error = re_compile_pattern(diaeresisPattern, strlen(diaeresisPattern),
373 		&diaeresisBuf);
374 	if (error)
375 		fprintf(stderr, error);
376 	error = re_compile_pattern(tildePattern, strlen(tildePattern), &tildeBuf);
377 	if (error)
378 		fprintf(stderr, error);
379 	error = re_compile_pattern(acutetabPattern, strlen(acutetabPattern),
380 		&acutetabBuf);
381 	if (error)
382 		fprintf(stderr, error);
383 	error = re_compile_pattern(gravetabPattern, strlen(gravetabPattern),
384 		&gravetabBuf);
385 	if (error)
386 		fprintf(stderr, error);
387 	error = re_compile_pattern(circumflextabPattern,
388 		strlen(circumflextabPattern), &circumflextabBuf);
389 	if (error)
390 		fprintf(stderr, error);
391 	error = re_compile_pattern(diaeresistabPattern, strlen(diaeresistabPattern),
392 		&diaeresistabBuf);
393 	if (error)
394 		fprintf(stderr, error);
395 	error = re_compile_pattern(tildetabPattern, strlen(tildetabPattern),
396 		&tildetabBuf);
397 	if (error)
398 		fprintf(stderr, error);
399 
400 	// Read file
401 
402 	delete[] fChars;
403 	fChars = new char[CHARS_TABLE_MAXSIZE];
404 	fCharsSize = CHARS_TABLE_MAXSIZE;
405 	int offset = 0;
406 	int acuteOffset = 0;
407 	int graveOffset = 0;
408 	int circumflexOffset = 0;
409 	int diaeresisOffset = 0;
410 	int tildeOffset = 0;
411 
412 	int32* maps[] = {
413 		fKeys.normal_map,
414 		fKeys.shift_map,
415 		fKeys.control_map,
416 		fKeys.option_map,
417 		fKeys.option_shift_map,
418 		fKeys.caps_map,
419 		fKeys.caps_shift_map,
420 		fKeys.option_caps_map,
421 		fKeys.option_caps_shift_map
422 	};
423 
424 	char buffer[1024];
425 
426 	while (fgets(buffer, sizeof(buffer) - 1, file) != NULL) {
427 		if (buffer[0] == '#' || buffer[0] == '\n')
428 			continue;
429 
430 		size_t length = strlen(buffer);
431 
432 		struct re_registers regs;
433 		if (re_search(&versionBuf, buffer, length, 0, length, &regs) >= 0) {
434 			sscanf(buffer + regs.start[1], "%" B_SCNu32, &fKeys.version);
435 		} else if (re_search(&capslockBuf, buffer, length, 0, length, &regs)
436 				>= 0) {
437 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &fKeys.caps_key);
438 		} else if (re_search(&scrolllockBuf, buffer, length, 0, length, &regs)
439 				>= 0) {
440 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &fKeys.scroll_key);
441 		} else if (re_search(&numlockBuf, buffer, length, 0, length, &regs)
442 				>= 0) {
443 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &fKeys.num_key);
444 		} else if (re_search(&lshiftBuf, buffer, length, 0, length, &regs)
445 				>= 0) {
446 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32,
447 				&fKeys.left_shift_key);
448 		} else if (re_search(&rshiftBuf, buffer, length, 0, length, &regs)
449 				>= 0) {
450 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32,
451 				&fKeys.right_shift_key);
452 		} else if (re_search(&lcommandBuf, buffer, length, 0, length, &regs)
453 				>= 0) {
454 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32,
455 				&fKeys.left_command_key);
456 		} else if (re_search(&rcommandBuf, buffer, length, 0, length, &regs)
457 				>= 0) {
458 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32,
459 				&fKeys.right_command_key);
460 		} else if (re_search(&lcontrolBuf, buffer, length, 0, length, &regs)
461 				>= 0) {
462 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32,
463 				&fKeys.left_control_key);
464 		} else if (re_search(&rcontrolBuf, buffer, length, 0, length, &regs)
465 				>= 0) {
466 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32,
467 				&fKeys.right_control_key);
468 		} else if (re_search(&loptionBuf, buffer, length, 0, length, &regs)
469 				>= 0) {
470 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32,
471 				&fKeys.left_option_key);
472 		} else if (re_search(&roptionBuf, buffer, length, 0, length, &regs)
473 				>= 0) {
474 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32,
475 				&fKeys.right_option_key);
476 		} else if (re_search(&menuBuf, buffer, length, 0, length, &regs) >= 0) {
477 			sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &fKeys.menu_key);
478 		} else if (re_search(&locksettingsBuf, buffer, length, 0, length, &regs)
479 				>= 0) {
480 			fKeys.lock_settings = 0;
481 			for (int32 i = 1; i <= 3; i++) {
482 				const char* start = buffer + regs.start[i];
483 				length = regs.end[i] - regs.start[i];
484 				if (length == 0)
485 					break;
486 
487 				if (!strncmp(start, "CapsLock", length))
488 					fKeys.lock_settings |= B_CAPS_LOCK;
489 				else if (!strncmp(start, "NumLock", length))
490 					fKeys.lock_settings |= B_NUM_LOCK;
491 				else if (!strncmp(start, "ScrollLock", length))
492 					fKeys.lock_settings |= B_SCROLL_LOCK;
493 			}
494 		} else if (re_search(&keyBuf, buffer, length, 0, length, &regs) >= 0) {
495 			uint32 keyCode;
496 			if (sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &keyCode) > 0) {
497 				for (int i = 2; i <= 10; i++) {
498 					maps[i - 2][keyCode] = offset;
499 					_ComputeChars(buffer, regs, i, offset);
500 				}
501 			}
502 		} else if (re_search(&acuteBuf, buffer, length, 0, length, &regs) >= 0) {
503 			for (int i = 1; i <= 2; i++) {
504 				fKeys.acute_dead_key[acuteOffset++] = offset;
505 				_ComputeChars(buffer, regs, i, offset);
506 			}
507 		} else if (re_search(&graveBuf, buffer, length, 0, length, &regs) >= 0) {
508 			for (int i = 1; i <= 2; i++) {
509 				fKeys.grave_dead_key[graveOffset++] = offset;
510 				_ComputeChars(buffer, regs, i, offset);
511 			}
512 		} else if (re_search(&circumflexBuf, buffer, length, 0, length, &regs)
513 				>= 0) {
514 			for (int i = 1; i <= 2; i++) {
515 				fKeys.circumflex_dead_key[circumflexOffset++] = offset;
516 				_ComputeChars(buffer, regs, i, offset);
517 			}
518 		} else if (re_search(&diaeresisBuf, buffer, length, 0, length, &regs)
519 				>= 0) {
520 			for (int i = 1; i <= 2; i++) {
521 				fKeys.dieresis_dead_key[diaeresisOffset++] = offset;
522 				_ComputeChars(buffer, regs, i, offset);
523 			}
524 		} else if (re_search(&tildeBuf, buffer, length, 0, length, &regs) >= 0) {
525 			for (int i = 1; i <= 2; i++) {
526 				fKeys.tilde_dead_key[tildeOffset++] = offset;
527 				_ComputeChars(buffer, regs, i, offset);
528 			}
529 		} else if (re_search(&acutetabBuf, buffer, length, 0, length, &regs)
530 				>= 0) {
531 			_ComputeTables(buffer, regs, fKeys.acute_tables);
532 		} else if (re_search(&gravetabBuf, buffer, length, 0, length, &regs)
533 				>= 0) {
534 			_ComputeTables(buffer, regs, fKeys.grave_tables);
535 		} else if (re_search(&circumflextabBuf, buffer, length, 0, length, &regs)
536 				>= 0) {
537 			_ComputeTables(buffer, regs, fKeys.circumflex_tables);
538 		} else if (re_search(&diaeresistabBuf, buffer, length, 0, length, &regs)
539 				>= 0) {
540 			_ComputeTables(buffer, regs, fKeys.dieresis_tables);
541 		} else if (re_search(&tildetabBuf, buffer, length, 0, length, &regs)
542 				>= 0) {
543 			_ComputeTables(buffer, regs, fKeys.tilde_tables);
544 		}
545 	}
546 
547 	fCharsSize = offset;
548 
549 	if (fKeys.version != 3)
550 		return KEYMAP_ERROR_UNKNOWN_VERSION;
551 
552 	return B_OK;
553 }
554 
555 
556 status_t
557 Keymap::SaveAsCurrent()
558 {
559 #if (defined(__BEOS__) || defined(__HAIKU__))
560 	BPath path;
561 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
562 	if (status != B_OK)
563 		return status;
564 
565 	path.Append("Key_map");
566 
567 	status = Save(path.Path());
568 	if (status != B_OK)
569 		return status;
570 
571 	Use();
572 	return B_OK;
573 #else	// ! __BEOS__
574 	fprintf(stderr, "Unsupported operation on this platform!\n");
575 	exit(1);
576 #endif	// ! __BEOS__
577 }
578 
579 
580 //! Save a binary keymap to a file.
581 status_t
582 Keymap::Save(const char* name)
583 {
584 	BFile file;
585 	status_t status = file.SetTo(name,
586 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
587 	if (status != B_OK)
588 		return status;
589 
590 	// convert to big endian
591 	for (uint32 i = 0; i < sizeof(fKeys) / sizeof(uint32); i++) {
592 		((uint32*)&fKeys)[i] = B_HOST_TO_BENDIAN_INT32(((uint32*)&fKeys)[i]);
593 	}
594 
595 	ssize_t bytesWritten = file.Write(&fKeys, sizeof(fKeys));
596 
597 	// convert endian back
598 	for (uint32 i = 0; i < sizeof(fKeys) / sizeof(uint32); i++) {
599 		((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
600 	}
601 
602 	if (bytesWritten < (ssize_t)sizeof(fKeys))
603 		return B_ERROR;
604 	if (bytesWritten < B_OK)
605 		return bytesWritten;
606 
607 	uint32 charSize = B_HOST_TO_BENDIAN_INT32(fCharsSize);
608 
609 	bytesWritten = file.Write(&charSize, sizeof(uint32));
610 	if (bytesWritten < (ssize_t)sizeof(uint32))
611 		return B_ERROR;
612 	if (bytesWritten < B_OK)
613 		return bytesWritten;
614 
615 	bytesWritten = file.Write(fChars, fCharsSize);
616 	if (bytesWritten < (ssize_t)fCharsSize)
617 		return B_ERROR;
618 	if (bytesWritten < B_OK)
619 		return bytesWritten;
620 
621 	return B_OK;
622 }
623 
624 
625 status_t
626 Keymap::SaveAsSource(const char* name)
627 {
628 	FILE* file = fopen(name, "w");
629 	if (file == NULL)
630 		return errno;
631 
632 #if (defined(__BEOS__) || defined(__HAIKU__))
633 	text_run_array* textRuns;
634 	_SaveSourceText(file, &textRuns);
635 
636 	if (textRuns != NULL) {
637 		ssize_t dataSize;
638 		void* data = BTextView::FlattenRunArray(textRuns, &dataSize);
639 		if (data != NULL) {
640 			BNode node(name);
641 			node.WriteAttr("styles", B_RAW_TYPE, 0, data, dataSize);
642 
643 			free(data);
644 		}
645 
646 		BTextView::FreeRunArray(textRuns);
647 	}
648 #else
649 	_SaveSourceText(file);
650 #endif
651 
652 	return B_OK;
653 }
654 
655 
656 status_t
657 Keymap::SaveAsSource(FILE* file)
658 {
659 	_SaveSourceText(file);
660 	return B_OK;
661 }
662 
663 
664 /*!	Save a keymap as C source file - this is used to get the default keymap
665 	into the input_server, for example.
666 	\a mapName is usually the path of the input keymap, and is used as the
667 	name of the keymap (the path will be removed, as well as its suffix).
668 */
669 status_t
670 Keymap::SaveAsCppHeader(const char* fileName, const char* mapName)
671 {
672 	BString name = mapName;
673 
674 	// cut off path
675 	int32 index = name.FindLast('/');
676 	if (index > 0)
677 		name.Remove(0, index + 1);
678 
679 	// prune ".keymap"
680 	index = name.FindLast('.');
681 	if (index > 0)
682 		name.Truncate(index);
683 
684 	FILE* file = fopen(fileName, "w");
685 	if (file == NULL)
686 		return errno;
687 
688 	fprintf(file, "/*\n"
689 		" * Haiku Keymap\n"
690 		" * This file is generated automatically. Don't edit!\n"
691 		" */\n\n");
692 	fprintf(file, "#include <InterfaceDefs.h>\n\n");
693 	fprintf(file, "const char *kSystemKeymapName = \"%s\";\n\n", name.String());
694 	fprintf(file, "const key_map kSystemKeymap = {\n");
695 	fprintf(file, "\tversion:%" B_PRIu32 ",\n", fKeys.version);
696 	fprintf(file, "\tcaps_key:0x%" B_PRIx32 ",\n", fKeys.caps_key);
697 	fprintf(file, "\tscroll_key:0x%" B_PRIx32 ",\n", fKeys.scroll_key);
698 	fprintf(file, "\tnum_key:0x%" B_PRIx32 ",\n", fKeys.num_key);
699 	fprintf(file, "\tleft_shift_key:0x%" B_PRIx32 ",\n", fKeys.left_shift_key);
700 	fprintf(file, "\tright_shift_key:0x%" B_PRIx32 ",\n",
701 		fKeys.right_shift_key);
702 	fprintf(file, "\tleft_command_key:0x%" B_PRIx32 ",\n",
703 		fKeys.left_command_key);
704 	fprintf(file, "\tright_command_key:0x%" B_PRIx32 ",\n",
705 		fKeys.right_command_key);
706 	fprintf(file, "\tleft_control_key:0x%" B_PRIx32 ",\n",
707 		fKeys.left_control_key);
708 	fprintf(file, "\tright_control_key:0x%" B_PRIx32 ",\n",
709 		fKeys.right_control_key);
710 	fprintf(file, "\tleft_option_key:0x%" B_PRIx32 ",\n",
711 		fKeys.left_option_key);
712 	fprintf(file, "\tright_option_key:0x%" B_PRIx32 ",\n",
713 		fKeys.right_option_key);
714 	fprintf(file, "\tmenu_key:0x%" B_PRIx32 ",\n", fKeys.menu_key);
715 	fprintf(file, "\tlock_settings:0x%" B_PRIx32 ",\n", fKeys.lock_settings);
716 
717 	dump_map(file, "control_map", fKeys.control_map);
718 	dump_map(file, "option_caps_shift_map", fKeys.option_caps_shift_map);
719 	dump_map(file, "option_caps_map", fKeys.option_caps_map);
720 	dump_map(file, "option_shift_map", fKeys.option_shift_map);
721 	dump_map(file, "option_map", fKeys.option_map);
722 	dump_map(file, "caps_shift_map", fKeys.caps_shift_map);
723 	dump_map(file, "caps_map", fKeys.caps_map);
724 	dump_map(file, "shift_map", fKeys.shift_map);
725 	dump_map(file, "normal_map", fKeys.normal_map);
726 
727 	dump_keys(file, "acute_dead_key", fKeys.acute_dead_key);
728 	dump_keys(file, "grave_dead_key", fKeys.grave_dead_key);
729 
730 	dump_keys(file, "circumflex_dead_key", fKeys.circumflex_dead_key);
731 	dump_keys(file, "dieresis_dead_key", fKeys.dieresis_dead_key);
732 	dump_keys(file, "tilde_dead_key", fKeys.tilde_dead_key);
733 
734 	fprintf(file, "\tacute_tables:0x%" B_PRIx32 ",\n", fKeys.acute_tables);
735 	fprintf(file, "\tgrave_tables:0x%" B_PRIx32 ",\n", fKeys.grave_tables);
736 	fprintf(file, "\tcircumflex_tables:0x%" B_PRIx32 ",\n",
737 		fKeys.circumflex_tables);
738 	fprintf(file, "\tdieresis_tables:0x%" B_PRIx32 ",\n",
739 		fKeys.dieresis_tables);
740 	fprintf(file, "\ttilde_tables:0x%" B_PRIx32 ",\n", fKeys.tilde_tables);
741 
742 	fprintf(file, "};\n\n");
743 
744 	fprintf(file, "const char kSystemKeyChars[] = {\n");
745 	for (uint32 i = 0; i < fCharsSize; i++) {
746 		if (i % 10 == 0) {
747 			if (i > 0)
748 				fprintf(file, "\n");
749 			fprintf(file, "\t");
750 		} else
751 			fprintf(file, " ");
752 
753 		fprintf(file, "0x%02x,", (uint8)fChars[i]);
754 	}
755 	fprintf(file, "\n};\n\n");
756 
757 	fprintf(file, "const uint32 kSystemKeyCharsSize = %" B_PRIu32 ";\n",
758 		fCharsSize);
759 	fclose(file);
760 
761 	return B_OK;
762 }
763 
764 
765 //! We make our input server use the map in /boot/home/config/settings/Keymap
766 status_t
767 Keymap::Use()
768 {
769 #if (defined(__BEOS__) || defined(__HAIKU__))
770 	return _restore_key_map_();
771 
772 #else	// ! __BEOS__
773 	fprintf(stderr, "Unsupported operation on this platform!\n");
774 	exit(1);
775 #endif	// ! __BEOS__
776 }
777 
778 
779 /*!	We need to know if a key is a modifier key to choose
780 	a valid key when several are pressed together
781 */
782 bool
783 Keymap::IsModifierKey(uint32 keyCode)
784 {
785 	return keyCode == fKeys.caps_key
786 		|| keyCode == fKeys.num_key
787 		|| keyCode == fKeys.left_shift_key
788 		|| keyCode == fKeys.right_shift_key
789 		|| keyCode == fKeys.left_command_key
790 		|| keyCode == fKeys.right_command_key
791 		|| keyCode == fKeys.left_control_key
792 		|| keyCode == fKeys.right_control_key
793 		|| keyCode == fKeys.left_option_key
794 		|| keyCode == fKeys.right_option_key
795 		|| keyCode == fKeys.menu_key;
796 }
797 
798 
799 //! Tell if a key is a dead key, needed for draw a dead key
800 uint8
801 Keymap::IsDeadKey(uint32 keyCode, uint32 modifiers)
802 {
803 	int32 offset;
804 	uint32 tableMask = 0;
805 
806 	switch (modifiers & 0xcf) {
807 		case B_SHIFT_KEY:
808 			offset = fKeys.shift_map[keyCode];
809 			tableMask = B_SHIFT_TABLE;
810 			break;
811 		case B_CAPS_LOCK:
812 			offset = fKeys.caps_map[keyCode];
813 			tableMask = B_CAPS_TABLE;
814 			break;
815 		case B_CAPS_LOCK | B_SHIFT_KEY:
816 			offset = fKeys.caps_shift_map[keyCode];
817 			tableMask = B_CAPS_SHIFT_TABLE;
818 			break;
819 		case B_OPTION_KEY:
820 			offset = fKeys.option_map[keyCode];
821 			tableMask = B_OPTION_TABLE;
822 			break;
823 		case B_OPTION_KEY | B_SHIFT_KEY:
824 			offset = fKeys.option_shift_map[keyCode];
825 			tableMask = B_OPTION_SHIFT_TABLE;
826 			break;
827 		case B_OPTION_KEY | B_CAPS_LOCK:
828 			offset = fKeys.option_caps_map[keyCode];
829 			tableMask = B_OPTION_CAPS_TABLE;
830 			break;
831 		case B_OPTION_KEY | B_SHIFT_KEY | B_CAPS_LOCK:
832 			offset = fKeys.option_caps_shift_map[keyCode];
833 			tableMask = B_OPTION_CAPS_SHIFT_TABLE;
834 			break;
835 		case B_CONTROL_KEY:
836 			offset = fKeys.control_map[keyCode];
837 			tableMask = B_CONTROL_TABLE;
838 			break;
839 
840 		default:
841 			offset = fKeys.normal_map[keyCode];
842 			tableMask = B_NORMAL_TABLE;
843 			break;
844 	}
845 
846 	if (offset <= 0)
847 		return 0;
848 	uint32 numBytes = fChars[offset];
849 
850 	if (!numBytes)
851 		return 0;
852 
853 	char chars[4];
854 	strncpy(chars, &(fChars[offset+1]), numBytes);
855 	chars[numBytes] = 0;
856 
857 	int32 deadOffsets[] = {
858 		fKeys.acute_dead_key[1],
859 		fKeys.grave_dead_key[1],
860 		fKeys.circumflex_dead_key[1],
861 		fKeys.dieresis_dead_key[1],
862 		fKeys.tilde_dead_key[1]
863 	};
864 
865 	uint32 deadTables[] = {
866 		fKeys.acute_tables,
867 		fKeys.grave_tables,
868 		fKeys.circumflex_tables,
869 		fKeys.dieresis_tables,
870 		fKeys.tilde_tables
871 	};
872 
873 	for (int32 i = 0; i < 5; i++) {
874 		if ((deadTables[i] & tableMask) == 0)
875 			continue;
876 
877 		if (offset == deadOffsets[i])
878 			return i+1;
879 
880 		uint32 deadNumBytes = fChars[deadOffsets[i]];
881 
882 		if (!deadNumBytes)
883 			continue;
884 
885 		if (strncmp(chars, &fChars[deadOffsets[i] + 1], deadNumBytes) == 0)
886 			return i + 1;
887 	}
888 	return 0;
889 }
890 
891 
892 //! Tell if a key is a dead second key, needed for draw a dead second key
893 bool
894 Keymap::IsDeadSecondKey(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey)
895 {
896 	if (!activeDeadKey)
897 		return false;
898 
899 	int32 offset;
900 
901 	switch (modifiers & 0xcf) {
902 		case B_SHIFT_KEY:
903 			offset = fKeys.shift_map[keyCode];
904 			break;
905 		case B_CAPS_LOCK:
906 			offset = fKeys.caps_map[keyCode];
907 			break;
908 		case B_CAPS_LOCK | B_SHIFT_KEY:
909 			offset = fKeys.caps_shift_map[keyCode];
910 			break;
911 		case B_OPTION_KEY:
912 			offset = fKeys.option_map[keyCode];
913 			break;
914 		case B_OPTION_KEY | B_SHIFT_KEY:
915 			offset = fKeys.option_shift_map[keyCode];
916 			break;
917 		case B_OPTION_KEY | B_CAPS_LOCK:
918 			offset = fKeys.option_caps_map[keyCode];
919 			break;
920 		case B_OPTION_KEY | B_SHIFT_KEY|B_CAPS_LOCK:
921 			offset = fKeys.option_caps_shift_map[keyCode];
922 			break;
923 		case B_CONTROL_KEY:
924 			offset = fKeys.control_map[keyCode];
925 			break;
926 
927 		default:
928 			offset = fKeys.normal_map[keyCode];
929 			break;
930 	}
931 
932 	uint32 numBytes = fChars[offset];
933 
934 	if (!numBytes)
935 		return false;
936 
937 	int32* deadOffsets[] = {
938 		fKeys.acute_dead_key,
939 		fKeys.grave_dead_key,
940 		fKeys.circumflex_dead_key,
941 		fKeys.dieresis_dead_key,
942 		fKeys.tilde_dead_key
943 	};
944 
945 	int32 *deadOffset = deadOffsets[activeDeadKey - 1];
946 
947 	for (int32 i = 0; i < 32; i++) {
948 		if (offset == deadOffset[i])
949 			return true;
950 
951 		uint32 deadNumBytes = fChars[deadOffset[i]];
952 
953 		if (!deadNumBytes)
954 			continue;
955 
956 		if (strncmp(&fChars[offset + 1], &fChars[deadOffset[i] + 1],
957 				deadNumBytes) == 0)
958 			return true;
959 
960 		i++;
961 	}
962 	return false;
963 }
964 
965 
966 //! Get the char for a key given modifiers and active dead key
967 void
968 Keymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey,
969 	char** chars, int32* numBytes)
970 {
971 	int32 offset;
972 
973 	*numBytes = 0;
974 	*chars = NULL;
975 
976 	// here we take NUMLOCK into account
977 	if (modifiers & B_NUM_LOCK) {
978 		switch (keyCode) {
979 			case 0x37:
980 			case 0x38:
981 			case 0x39:
982 			case 0x48:
983 			case 0x49:
984 			case 0x4a:
985 			case 0x58:
986 			case 0x59:
987 			case 0x5a:
988 			case 0x64:
989 			case 0x65:
990 				modifiers ^= B_SHIFT_KEY;
991 		}
992 	}
993 
994 	// here we choose the right map given the modifiers
995 	switch (modifiers & 0xcf) {
996 		case B_SHIFT_KEY:
997 			offset = fKeys.shift_map[keyCode];
998 			break;
999 		case B_CAPS_LOCK:
1000 			offset = fKeys.caps_map[keyCode];
1001 			break;
1002 		case B_CAPS_LOCK | B_SHIFT_KEY:
1003 			offset = fKeys.caps_shift_map[keyCode];
1004 			break;
1005 		case B_OPTION_KEY:
1006 			offset = fKeys.option_map[keyCode];
1007 			break;
1008 		case B_OPTION_KEY | B_SHIFT_KEY:
1009 			offset = fKeys.option_shift_map[keyCode];
1010 			break;
1011 		case B_OPTION_KEY | B_CAPS_LOCK:
1012 			offset = fKeys.option_caps_map[keyCode];
1013 			break;
1014 		case B_OPTION_KEY | B_SHIFT_KEY | B_CAPS_LOCK:
1015 			offset = fKeys.option_caps_shift_map[keyCode];
1016 			break;
1017 		case B_CONTROL_KEY:
1018 			offset = fKeys.control_map[keyCode];
1019 			break;
1020 
1021 		default:
1022 			offset = fKeys.normal_map[keyCode];
1023 			break;
1024 	}
1025 
1026 	// here we get the char size
1027 	*numBytes = fChars[offset];
1028 
1029 	if (!*numBytes)
1030 		return;
1031 
1032 	// here we take an potential active dead key
1033 	int32* deadKey;
1034 	switch (activeDeadKey) {
1035 		case 1:
1036 			deadKey = fKeys.acute_dead_key;
1037 			break;
1038 		case 2:
1039 			deadKey = fKeys.grave_dead_key;
1040 			break;
1041 		case 3:
1042 			deadKey = fKeys.circumflex_dead_key;
1043 			break;
1044 		case 4:
1045 			deadKey = fKeys.dieresis_dead_key;
1046 			break;
1047 		case 5:
1048 			deadKey = fKeys.tilde_dead_key;
1049 			break;
1050 
1051 		default:
1052 			// if not dead, we copy and return the char
1053 			char *str = *chars = new char[*numBytes + 1];
1054 			strncpy(str, &fChars[offset + 1], *numBytes);
1055 			str[*numBytes] = 0;
1056 			return;
1057 	}
1058 
1059 	// If dead key, we search for our current offset char in the dead key offset
1060 	// table string comparison is needed
1061 	for (int32 i = 0; i < 32; i++) {
1062 		if (!strncmp(&fChars[offset + 1], &fChars[deadKey[i] + 1], *numBytes)) {
1063 			*numBytes = fChars[deadKey[i + 1]];
1064 
1065 			switch (*numBytes) {
1066 				case 0:
1067 					// Not mapped
1068 					*chars = NULL;
1069 					break;
1070 
1071 				default:
1072 					// 1-, 2-, 3-, or 4-byte UTF-8 character
1073 					char* str = *chars = new char[*numBytes + 1];
1074 					strncpy(str, &fChars[deadKey[i + 1] + 1], *numBytes);
1075 					str[*numBytes] = 0;
1076 					break;
1077 			}
1078 			return;
1079 		}
1080 		i++;
1081 	}
1082 
1083 	// if not found we return the current char mapped
1084 	*chars = new char[*numBytes + 1];
1085 	strncpy(*chars, &(fChars[offset+1]), *numBytes);
1086 	(*chars)[*numBytes] = 0;
1087 }
1088 
1089 
1090 void
1091 Keymap::RestoreSystemDefault()
1092 {
1093 #if (defined(__BEOS__) || defined(__HAIKU__))
1094 	// work-around to get rid of this stupid find_directory_r() on Zeta
1095 #	ifdef find_directory
1096 #		undef find_directory
1097 #	endif
1098 	BPath path;
1099 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
1100 		return;
1101 
1102 	path.Append("Key_map");
1103 
1104 	BEntry entry(path.Path());
1105 	entry.Remove();
1106 
1107 	_restore_key_map_();
1108 #else	// ! __BEOS__
1109 	fprintf(stderr, "Unsupported operation on this platform!\n");
1110 	exit(1);
1111 #endif	// ! __BEOS__
1112 }
1113 
1114 
1115 /*static*/ bool
1116 Keymap::GetKey(const char* chars, int32 offset, char* buffer, size_t bufferSize)
1117 {
1118 	uint8 size = (uint8)chars[offset++];
1119 	char string[1024];
1120 
1121 	switch (size) {
1122 		case 0:
1123 			// Not mapped
1124 			strlcpy(buffer, "''", bufferSize);
1125 			return false;
1126 
1127 		case 1:
1128 			// 1-byte UTF-8/ASCII character
1129 			if ((uint8)chars[offset] < 0x20 || (uint8)chars[offset] > 0x7e)
1130 				sprintf(string, "0x%02x", (uint8)chars[offset]);
1131 			else {
1132 				sprintf(string, "'%s%c'",
1133 					(chars[offset] == '\\' || chars[offset] == '\'') ? "\\" : "",
1134 					chars[offset]);
1135 			}
1136 			break;
1137 
1138 		default:
1139 			// multi-byte UTF-8 character
1140 			sprintf(string, "0x");
1141 			for (int i = 0; i < size; i++) {
1142 				sprintf(string + 2 * (i + 1), "%02x", (uint8)chars[offset + i]);
1143 			}
1144 			break;
1145 	}
1146 
1147 	strlcpy(buffer, string, bufferSize);
1148 	return true;
1149 }
1150 
1151 
1152 #if (defined(__BEOS__) || defined(__HAIKU__))
1153 void
1154 Keymap::_SaveSourceText(FILE* file, text_run_array** _textRuns)
1155 {
1156 	text_run_array* runs = NULL;
1157 	if (_textRuns != NULL) {
1158 		runs = BTextView::AllocRunArray(8);
1159 		*_textRuns = runs;
1160 	}
1161 #else
1162 void
1163 Keymap::_SaveSourceText(FILE* file)
1164 {
1165 #endif
1166 
1167 	static const rgb_color kCommentColor = (rgb_color){200, 92, 92, 255};
1168 	static const rgb_color kTextColor = (rgb_color){0, 0, 0, 255};
1169 
1170 #if (defined(__BEOS__) || defined(__HAIKU__))
1171 	BFont font = *be_fixed_font;
1172 
1173 	if (runs != NULL) {
1174 		runs->runs[0].offset = 0;
1175 		runs->runs[0].font = font;
1176 		runs->runs[0].color = kCommentColor;
1177 	}
1178 #endif
1179 
1180 	int bytes = fprintf(file, "#!/bin/keymap -l\n"
1181 		"#\n"
1182 		"#\tRaw key numbering for 101 keyboard...\n");
1183 
1184 #if (defined(__BEOS__) || defined(__HAIKU__))
1185 	if (runs != NULL) {
1186 		runs->runs[1].offset = bytes;
1187 		runs->runs[1].font = font;
1188 		runs->runs[1].font.SetSize(9);
1189 		runs->runs[1].color = kCommentColor;
1190 	}
1191 #endif
1192 
1193 	bytes += fprintf(file, "#                                                                                        [sys]       [brk]\n"
1194 		"#                                                                                         0x7e        0x7f\n"
1195 		"# [esc]       [ f1] [ f2] [ f3] [ f4] [ f5] [ f6] [ f7] [ f8] [ f9] [f10] [f11] [f12]    [prn] [scr] [pau]\n"
1196 		"#  0x01        0x02  0x03  0x04  0x05  0x06  0x07  0x08  0x09  0x0a  0x0b  0x0c  0x0d     0x0e  0x0f  0x10     K E Y P A D   K E Y S\n"
1197 		"#\n"
1198 		"# [ ` ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 0 ] [ - ] [ = ] [bck]    [ins] [hme] [pup]    [num] [ / ] [ * ] [ - ]\n"
1199 		"#  0x11  0x12  0x13  0x14  0x15  0x16  0x17  0x18  0x19  0x1a  0x1b  0x1c  0x1d  0x1e     0x1f  0x20  0x21     0x22  0x23  0x24  0x25\n"
1200 		"#\n"
1201 		"# [tab] [ q ] [ w ] [ e ] [ r ] [ t ] [ y ] [ u ] [ i ] [ o ] [ p ] [ [ ] [ ] ] [ \\ ]    [del] [end] [pdn]    [ 7 ] [ 8 ] [ 9 ] [ + ]\n"
1202 		"#  0x26  0x27  0x28  0x29  0x2a  0x2b  0x2c  0x2d  0x2e  0x2f  0x30  0x31  0x32  0x33     0x34  0x35  0x36     0x37  0x38  0x39  0x3a\n"
1203 		"#\n"
1204 		"# [cap] [ a ] [ s ] [ d ] [ f ] [ g ] [ h ] [ j ] [ k ] [ l ] [ ; ] [ ' ] [  enter  ]                         [ 4 ] [ 5 ] [ 6 ]\n"
1205 		"#  0x3b  0x3c  0x3d  0x3e  0x3f  0x40  0x41  0x42  0x43  0x44  0x45  0x46     0x47                             0x48  0x49  0x4a\n"
1206 		"#\n"
1207 		"# [shift]     [ z ] [ x ] [ c ] [ v ] [ b ] [ n ] [ m ] [ , ] [ . ] [ / ]     [shift]          [ up]          [ 1 ] [ 2 ] [ 3 ] [ent]\n"
1208 		"#   0x4b       0x4c  0x4d  0x4e  0x4f  0x50  0x51  0x52  0x53  0x54  0x55       0x56            0x57           0x58  0x59  0x5a  0x5b\n"
1209 		"#\n"
1210 		"# [ctr]             [cmd]             [  space  ]             [cmd]             [ctr]    [lft] [dwn] [rgt]    [ 0 ] [ . ]\n"
1211 		"#  0x5c              0x5d                 0x5e                 0x5f              0x60     0x61  0x62  0x63     0x64  0x65\n");
1212 
1213 #if (defined(__BEOS__) || defined(__HAIKU__))
1214 	if (runs != NULL) {
1215 		runs->runs[2].offset = bytes;
1216 		runs->runs[2].font = font;
1217 		runs->runs[2].color = kCommentColor;
1218 	}
1219 #endif
1220 
1221 	bytes += fprintf(file, "#\n"
1222 		"#\tNOTE: On a Microsoft Natural Keyboard:\n"
1223 		"#\t\t\tleft option  = 0x66\n"
1224 		"#\t\t\tright option = 0x67\n"
1225 		"#\t\t\tmenu key     = 0x68\n"
1226 		"#\tNOTE: On an Apple Extended Keyboard:\n"
1227 		"#\t\t\tleft option  = 0x66\n"
1228 		"#\t\t\tright option = 0x67\n"
1229 		"#\t\t\tkeypad '='   = 0x6a\n"
1230 		"#\t\t\tpower key    = 0x6b\n");
1231 
1232 #if (defined(__BEOS__) || defined(__HAIKU__))
1233 	if (runs != NULL) {
1234 		runs->runs[3].offset = bytes;
1235 		runs->runs[3].font = *be_fixed_font;
1236 		runs->runs[3].color = kTextColor;
1237 	}
1238 #endif
1239 
1240 	bytes += fprintf(file, "Version = %" B_PRIu32 "\n"
1241 		"CapsLock = 0x%02" B_PRIx32 "\n"
1242 		"ScrollLock = 0x%02" B_PRIx32 "\n"
1243 		"NumLock = 0x%02" B_PRIx32 "\n"
1244 		"LShift = 0x%02" B_PRIx32 "\n"
1245 		"RShift = 0x%02" B_PRIx32 "\n"
1246 		"LCommand = 0x%02" B_PRIx32 "\n"
1247 		"RCommand = 0x%02" B_PRIx32 "\n"
1248 		"LControl = 0x%02" B_PRIx32 "\n"
1249 		"RControl = 0x%02" B_PRIx32 "\n"
1250 		"LOption = 0x%02" B_PRIx32 "\n"
1251 		"ROption = 0x%02" B_PRIx32 "\n"
1252 		"Menu = 0x%02" B_PRIx32 "\n",
1253 		fKeys.version, fKeys.caps_key, fKeys.scroll_key, fKeys.num_key,
1254 		fKeys.left_shift_key, fKeys.right_shift_key, fKeys.left_command_key,
1255 		fKeys.right_command_key, fKeys.left_control_key, fKeys.right_control_key,
1256 		fKeys.left_option_key, fKeys.right_option_key, fKeys.menu_key);
1257 
1258 #if (defined(__BEOS__) || defined(__HAIKU__))
1259 	if (runs != NULL) {
1260 		runs->runs[4].offset = bytes;
1261 		runs->runs[4].font = *be_fixed_font;
1262 		runs->runs[4].color = kCommentColor;
1263 	}
1264 #endif
1265 
1266 	bytes += fprintf(file, "#\n"
1267 		"# Lock settings\n"
1268 		"# To set NumLock, do the following:\n"
1269 		"#   LockSettings = NumLock\n"
1270 		"#\n"
1271 		"# To set everything, do the following:\n"
1272 		"#   LockSettings = CapsLock NumLock ScrollLock\n"
1273 		"#\n");
1274 
1275 #if (defined(__BEOS__) || defined(__HAIKU__))
1276 	if (runs != NULL) {
1277 		runs->runs[5].offset = bytes;
1278 		runs->runs[5].font = *be_fixed_font;
1279 		runs->runs[5].color = kTextColor;
1280 	}
1281 #endif
1282 
1283 	bytes += fprintf(file, "LockSettings = ");
1284 	if ((fKeys.lock_settings & B_CAPS_LOCK) != 0)
1285 		bytes += fprintf(file, "CapsLock ");
1286 	if ((fKeys.lock_settings & B_NUM_LOCK) != 0)
1287 		bytes += fprintf(file, "NumLock ");
1288 	if ((fKeys.lock_settings & B_SCROLL_LOCK) != 0)
1289 		bytes += fprintf(file, "ScrollLock ");
1290 	bytes += fprintf(file, "\n");
1291 
1292 #if (defined(__BEOS__) || defined(__HAIKU__))
1293 	if (runs != NULL) {
1294 		runs->runs[6].offset = bytes;
1295 		runs->runs[6].font = *be_fixed_font;
1296 		runs->runs[6].color = kCommentColor;
1297 	}
1298 #endif
1299 
1300 	bytes += fprintf(file, "# Legend:\n"
1301 		"#   n = Normal\n"
1302 		"#   s = Shift\n"
1303 		"#   c = Control\n"
1304 		"#   C = CapsLock\n"
1305 		"#   o = Option\n"
1306 		"# Key      n        s        c        o        os       C        Cs       Co       Cos     \n");
1307 
1308 #if (defined(__BEOS__) || defined(__HAIKU__))
1309 	if (runs != NULL) {
1310 		runs->runs[7].offset = bytes;
1311 		runs->runs[7].font = *be_fixed_font;
1312 		runs->runs[7].color = kTextColor;
1313 	}
1314 #endif
1315 
1316 	for (int i = 0; i < 128; i++) {
1317 		char normalKey[32];
1318 		char shiftKey[32];
1319 		char controlKey[32];
1320 		char optionKey[32];
1321 		char optionShiftKey[32];
1322 		char capsKey[32];
1323 		char capsShiftKey[32];
1324 		char optionCapsKey[32];
1325 		char optionCapsShiftKey[32];
1326 
1327 		GetKey(fChars, fKeys.normal_map[i], normalKey, 32);
1328 		GetKey(fChars, fKeys.shift_map[i], shiftKey, 32);
1329 		GetKey(fChars, fKeys.control_map[i], controlKey, 32);
1330 		GetKey(fChars, fKeys.option_map[i], optionKey, 32);
1331 		GetKey(fChars, fKeys.option_shift_map[i], optionShiftKey, 32);
1332 		GetKey(fChars, fKeys.caps_map[i], capsKey, 32);
1333 		GetKey(fChars, fKeys.caps_shift_map[i], capsShiftKey, 32);
1334 		GetKey(fChars, fKeys.option_caps_map[i], optionCapsKey, 32);
1335 		GetKey(fChars, fKeys.option_caps_shift_map[i], optionCapsShiftKey, 32);
1336 
1337 		fprintf(file, "Key 0x%02x = %-9s%-9s%-9s%-9s%-9s%-9s%-9s%-9s%-9s\n", i,
1338 			normalKey, shiftKey, controlKey, optionKey, optionShiftKey, capsKey,
1339 			capsShiftKey, optionCapsKey, optionCapsShiftKey);
1340 	}
1341 
1342 	int32* deadOffsets[] = {
1343 		fKeys.acute_dead_key,
1344 		fKeys.grave_dead_key,
1345 		fKeys.circumflex_dead_key,
1346 		fKeys.dieresis_dead_key,
1347 		fKeys.tilde_dead_key
1348 	};
1349 
1350 	char labels[][12] = {
1351 		"Acute",
1352 		"Grave",
1353 		"Circumflex",
1354 		"Diaeresis",
1355 		"Tilde"
1356 	};
1357 
1358 	uint32 deadTables[] = {
1359 		fKeys.acute_tables,
1360 		fKeys.grave_tables,
1361 		fKeys.circumflex_tables,
1362 		fKeys.dieresis_tables,
1363 		fKeys.tilde_tables
1364 	};
1365 
1366 	for (int i = 0; i < 5; i++) {
1367 		for (int deadIndex = 0; deadIndex < 32; deadIndex++) {
1368 			char deadKey[32];
1369 			char secondKey[32];
1370 			if (!GetKey(fChars, deadOffsets[i][deadIndex++], deadKey, 32))
1371 				break;
1372 
1373 			GetKey(fChars, deadOffsets[i][deadIndex], secondKey, 32);
1374 			fprintf(file, "%s %-9s = %-9s\n", labels[i], deadKey, secondKey);
1375 		}
1376 
1377 		fprintf(file, "%sTab = ", labels[i]);
1378 
1379 		if (deadTables[i] & B_NORMAL_TABLE)
1380 			fprintf(file, "Normal ");
1381 		if (deadTables[i] & B_SHIFT_TABLE)
1382 			fprintf(file, "Shift ");
1383 		if (deadTables[i] & B_CONTROL_TABLE)
1384 			fprintf(file, "Control ");
1385 		if (deadTables[i] & B_OPTION_TABLE)
1386 			fprintf(file, "Option ");
1387 		if (deadTables[i] & B_OPTION_SHIFT_TABLE)
1388 			fprintf(file, "Option-Shift ");
1389 		if (deadTables[i] & B_CAPS_TABLE)
1390 			fprintf(file, "CapsLock ");
1391 		if (deadTables[i] & B_CAPS_SHIFT_TABLE)
1392 			fprintf(file, "CapsLock-Shift ");
1393 		if (deadTables[i] & B_OPTION_CAPS_TABLE)
1394 			fprintf(file, "CapsLock-Option ");
1395 		if (deadTables[i] & B_OPTION_CAPS_SHIFT_TABLE)
1396 			fprintf(file, "CapsLock-Option-Shift ");
1397 		fprintf(file, "\n");
1398 	}
1399 }
1400 
1401 
1402 void
1403 Keymap::_ComputeChars(const char* buffer, struct re_registers& regs, int i,
1404 	int& offset)
1405 {
1406 	char* current = &fChars[offset + 1];
1407 	char hexChars[12];
1408 	uint32 length = 0;
1409 
1410 	if (strncmp(buffer + regs.start[i], "''", regs.end[i] - regs.start[i]) == 0)
1411 		length = 0;
1412 	else if (sscanf(buffer + regs.start[i], "'%s'", current) > 0) {
1413 		if (current[0] == '\\')
1414 			current[0] = current[1];
1415 		else if (current[0] == '\'')
1416 			current[0] = ' ';
1417 		length = 1;
1418 	} else if (sscanf(buffer + regs.start[i], "0x%s", hexChars) > 0) {
1419 		length = strlen(hexChars) / 2;
1420 		for (uint32 j = 0; j < length; j++)
1421 			sscanf(hexChars + 2*j, "%02hhx", current + j);
1422 	}
1423 
1424 	fChars[offset] = length;
1425 	offset += length + 1;
1426 }
1427 
1428 
1429 void
1430 Keymap::_ComputeTables(const char* buffer, struct re_registers& regs,
1431 	uint32& table)
1432 {
1433 	for (int32 i = 1; i <= 9; i++) {
1434 		int32 length = regs.end[i] - regs.start[i];
1435 		if (length <= 0)
1436 			break;
1437 
1438 		const char* start = buffer + regs.start[i];
1439 
1440 		if (strncmp(start, "Normal", length) == 0)
1441 			table |= B_NORMAL_TABLE;
1442 		else if (strncmp(start, "Shift", length) == 0)
1443 			table |= B_SHIFT_TABLE;
1444 		else if (strncmp(start, "Control", length) == 0)
1445 			table |= B_CONTROL_TABLE;
1446 		else if (strncmp(start, "Option", length) == 0)
1447 			table |= B_OPTION_TABLE;
1448 		else if (strncmp(start, "Option-Shift", length) == 0)
1449 			table |= B_OPTION_SHIFT_TABLE;
1450 		else if (strncmp(start, "CapsLock", length) == 0)
1451 			table |= B_CAPS_TABLE;
1452 		else if (strncmp(start, "CapsLock-Shift", length) == 0)
1453 			table |= B_CAPS_SHIFT_TABLE;
1454 		else if (strncmp(start, "CapsLock-Option", length) == 0)
1455 			table |= B_OPTION_CAPS_TABLE;
1456 		else if (strncmp(start, "CapsLock-Option-Shift", length) == 0)
1457 			table |= B_OPTION_CAPS_SHIFT_TABLE;
1458 	}
1459 }
1460