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