vfs: check userland buffers before reading them.
[haiku.git] / src / kits / shared / Keymap.cpp
blob65fd7cc43f683e82e136a7af297a7885243f4fee
1 /*
2 * Copyright 2004-2012, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Jérôme Duval
7 * Axel Dörfler, axeld@pinc-software.de.
8 * John Scipione, jscipione@gmail.com.
9 */
12 #include <Keymap.h>
14 #include <new>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include <ByteOrder.h>
21 #include <File.h>
24 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
25 # include "SystemKeymap.h"
26 // generated by the build system
27 #endif
30 // Private only at this point, as we might want to improve the dead key
31 // implementation in the future
32 enum dead_key_index {
33 kDeadKeyAcute = 1,
34 kDeadKeyGrave,
35 kDeadKeyCircumflex,
36 kDeadKeyDiaeresis,
37 kDeadKeyTilde
41 static const uint32 kModifierKeys = B_SHIFT_KEY | B_CAPS_LOCK | B_CONTROL_KEY
42 | B_OPTION_KEY | B_COMMAND_KEY | B_MENU_KEY;
45 BKeymap::BKeymap()
47 fChars(NULL),
48 fCharsSize(0)
50 Unset();
54 BKeymap::~BKeymap()
56 delete[] fChars;
60 /*! Load a map from a file.
61 File format in big endian:
62 struct key_map
63 uint32 size of following charset
64 charset (offsets go into this with size of character followed by
65 character)
67 status_t
68 BKeymap::SetTo(const char* path)
70 BFile file;
71 status_t status = file.SetTo(path, B_READ_ONLY);
72 if (status != B_OK)
73 return status;
75 return SetTo(file);
79 status_t
80 BKeymap::SetTo(BDataIO& stream)
82 if (stream.Read(&fKeys, sizeof(fKeys)) < 1)
83 return B_IO_ERROR;
85 // convert from big-endian
86 for (uint32 i = 0; i < sizeof(fKeys) / 4; i++) {
87 ((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
90 if (fKeys.version != 3)
91 return B_BAD_DATA;
93 if (stream.Read(&fCharsSize, sizeof(uint32)) < 1)
94 return B_IO_ERROR;
96 fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
97 if (fCharsSize > 16 * 1024) {
98 Unset();
99 return B_BAD_DATA;
102 delete[] fChars;
103 fChars = new char[fCharsSize];
105 if (stream.Read(fChars, fCharsSize) != (ssize_t)fCharsSize) {
106 Unset();
107 return B_IO_ERROR;
110 return B_OK;
114 status_t
115 BKeymap::SetToCurrent()
117 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
118 key_map* keys = NULL;
119 get_key_map(&keys, &fChars);
120 if (!keys)
121 return B_ERROR;
123 memcpy(&fKeys, keys, sizeof(fKeys));
124 free(keys);
126 fCharsSize = sizeof(fChars);
128 return B_OK;
129 #else // ! __BEOS__
130 fprintf(stderr, "Unsupported operation on this platform!\n");
131 exit(1);
132 #endif // ! __BEOS__
136 status_t
137 BKeymap::SetToDefault()
139 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
140 fKeys = kSystemKeymap;
141 fCharsSize = kSystemKeyCharsSize;
143 fChars = new (std::nothrow) char[fCharsSize];
144 if (fChars == NULL) {
145 Unset();
146 return B_NO_MEMORY;
149 memcpy(fChars, kSystemKeyChars, fCharsSize);
150 return B_OK;
151 #else // ! __BEOS__
152 fprintf(stderr, "Unsupported operation on this platform!\n");
153 exit(1);
154 #endif // ! __BEOS__
158 void
159 BKeymap::Unset()
161 delete[] fChars;
162 fChars = NULL;
163 fCharsSize = 0;
165 memset(&fKeys, 0, sizeof(fKeys));
169 /*! We need to know if a key is a modifier key to choose
170 a valid key when several are pressed together
172 bool
173 BKeymap::IsModifierKey(uint32 keyCode) const
175 return keyCode == fKeys.caps_key
176 || keyCode == fKeys.num_key
177 || keyCode == fKeys.scroll_key
178 || keyCode == fKeys.left_shift_key
179 || keyCode == fKeys.right_shift_key
180 || keyCode == fKeys.left_command_key
181 || keyCode == fKeys.right_command_key
182 || keyCode == fKeys.left_control_key
183 || keyCode == fKeys.right_control_key
184 || keyCode == fKeys.left_option_key
185 || keyCode == fKeys.right_option_key
186 || keyCode == fKeys.menu_key;
190 //! We need to know a modifier for a key
191 uint32
192 BKeymap::Modifier(uint32 keyCode) const
194 if (keyCode == fKeys.caps_key)
195 return B_CAPS_LOCK;
196 if (keyCode == fKeys.num_key)
197 return B_NUM_LOCK;
198 if (keyCode == fKeys.scroll_key)
199 return B_SCROLL_LOCK;
200 if (keyCode == fKeys.left_shift_key)
201 return B_LEFT_SHIFT_KEY | B_SHIFT_KEY;
202 if (keyCode == fKeys.right_shift_key)
203 return B_RIGHT_SHIFT_KEY | B_SHIFT_KEY;
204 if (keyCode == fKeys.left_command_key)
205 return B_LEFT_COMMAND_KEY | B_COMMAND_KEY;
206 if (keyCode == fKeys.right_command_key)
207 return B_RIGHT_COMMAND_KEY | B_COMMAND_KEY;
208 if (keyCode == fKeys.left_control_key)
209 return B_LEFT_CONTROL_KEY | B_CONTROL_KEY;
210 if (keyCode == fKeys.right_control_key)
211 return B_RIGHT_CONTROL_KEY | B_CONTROL_KEY;
212 if (keyCode == fKeys.left_option_key)
213 return B_LEFT_OPTION_KEY | B_OPTION_KEY;
214 if (keyCode == fKeys.right_option_key)
215 return B_RIGHT_OPTION_KEY | B_OPTION_KEY;
216 if (keyCode == fKeys.menu_key)
217 return B_MENU_KEY;
219 return 0;
223 uint32
224 BKeymap::KeyForModifier(uint32 modifier) const
226 if (modifier == B_CAPS_LOCK)
227 return fKeys.caps_key;
228 if (modifier == B_NUM_LOCK)
229 return fKeys.num_key;
230 if (modifier == B_SCROLL_LOCK)
231 return fKeys.scroll_key;
232 if (modifier == B_LEFT_SHIFT_KEY || modifier == B_SHIFT_KEY)
233 return fKeys.left_shift_key;
234 if (modifier == B_RIGHT_SHIFT_KEY)
235 return fKeys.right_shift_key;
236 if (modifier == B_LEFT_COMMAND_KEY || modifier == B_COMMAND_KEY)
237 return fKeys.left_command_key;
238 if (modifier == B_RIGHT_COMMAND_KEY)
239 return fKeys.right_command_key;
240 if (modifier == B_LEFT_CONTROL_KEY || modifier == B_CONTROL_KEY)
241 return fKeys.left_control_key;
242 if (modifier == B_RIGHT_CONTROL_KEY)
243 return fKeys.right_control_key;
244 if (modifier == B_LEFT_OPTION_KEY || modifier == B_OPTION_KEY)
245 return fKeys.left_option_key;
246 if (modifier == B_RIGHT_OPTION_KEY)
247 return fKeys.right_option_key;
248 if (modifier == B_MENU_KEY)
249 return fKeys.menu_key;
251 return 0;
255 /*! Checks whether a key is an active dead key.
257 uint8
258 BKeymap::ActiveDeadKey(uint32 keyCode, uint32 modifiers) const
260 bool enabled;
261 uint8 deadKey = DeadKey(keyCode, modifiers, &enabled);
262 if (deadKey == 0 || !enabled)
263 return 0;
265 return deadKey;
269 /*! Checks whether a key is a dead key.
270 If it is, the enabled/disabled state of that dead key will be passed
271 out via isEnabled (isEnabled is not touched for non-dead keys).
273 uint8
274 BKeymap::DeadKey(uint32 keyCode, uint32 modifiers, bool* _isEnabled) const
276 uint32 tableMask = 0;
277 int32 offset = Offset(keyCode, modifiers, &tableMask);
278 uint8 deadKeyIndex = DeadKeyIndex(offset);
279 if (deadKeyIndex > 0 && _isEnabled != NULL) {
280 uint32 deadTables[] = {
281 fKeys.acute_tables,
282 fKeys.grave_tables,
283 fKeys.circumflex_tables,
284 fKeys.dieresis_tables,
285 fKeys.tilde_tables
287 *_isEnabled = (deadTables[deadKeyIndex - 1] & tableMask) != 0;
290 return deadKeyIndex;
294 //! Tell if a key is a dead second key.
295 bool
296 BKeymap::IsDeadSecondKey(uint32 keyCode, uint32 modifiers,
297 uint8 activeDeadKey) const
299 if (!activeDeadKey)
300 return false;
302 int32 offset = Offset(keyCode, modifiers);
303 if (offset < 0)
304 return false;
306 uint32 numBytes = fChars[offset];
307 if (!numBytes)
308 return false;
310 const int32* deadOffsets[] = {
311 fKeys.acute_dead_key,
312 fKeys.grave_dead_key,
313 fKeys.circumflex_dead_key,
314 fKeys.dieresis_dead_key,
315 fKeys.tilde_dead_key
318 const int32* deadOffset = deadOffsets[activeDeadKey - 1];
320 for (int32 i = 0; i < 32; i++) {
321 if (offset == deadOffset[i])
322 return true;
324 uint32 deadNumBytes = fChars[deadOffset[i]];
326 if (!deadNumBytes)
327 continue;
329 if (strncmp(&fChars[offset + 1], &fChars[deadOffset[i] + 1],
330 deadNumBytes) == 0)
331 return true;
332 i++;
334 return false;
338 //! Get the char for a key given modifiers and active dead key
339 void
340 BKeymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey,
341 char** chars, int32* numBytes) const
343 *numBytes = 0;
344 *chars = NULL;
346 if (keyCode > 128 || fChars == NULL)
347 return;
349 // here we take NUMLOCK into account
350 if ((modifiers & B_NUM_LOCK) != 0) {
351 switch (keyCode) {
352 case 0x37:
353 case 0x38:
354 case 0x39:
355 case 0x48:
356 case 0x49:
357 case 0x4a:
358 case 0x58:
359 case 0x59:
360 case 0x5a:
361 case 0x64:
362 case 0x65:
363 modifiers ^= B_SHIFT_KEY;
367 int32 offset = Offset(keyCode, modifiers);
368 if (offset < 0)
369 return;
371 // here we get the char size
372 *numBytes = fChars[offset];
373 if (*numBytes <= 0) {
374 // if key is not mapped in the option table, fall-through.
375 if ((modifiers & B_OPTION_KEY) != 0) {
376 offset = Offset(keyCode, modifiers & ~B_OPTION_KEY);
377 if (offset < 0)
378 return;
379 // get the char size again
380 *numBytes = fChars[offset];
381 if (*numBytes <= 0)
382 return;
383 } else
384 return;
387 // here we take an potential active dead key
388 const int32* deadKey;
389 switch (activeDeadKey) {
390 case kDeadKeyAcute:
391 deadKey = fKeys.acute_dead_key;
392 break;
393 case kDeadKeyGrave:
394 deadKey = fKeys.grave_dead_key;
395 break;
396 case kDeadKeyCircumflex:
397 deadKey = fKeys.circumflex_dead_key;
398 break;
399 case kDeadKeyDiaeresis:
400 deadKey = fKeys.dieresis_dead_key;
401 break;
402 case kDeadKeyTilde:
403 deadKey = fKeys.tilde_dead_key;
404 break;
405 default:
407 // if not dead, we copy and return the char
408 char* str = *chars = new char[*numBytes + 1];
409 strncpy(str, &fChars[offset + 1], *numBytes);
410 str[*numBytes] = 0;
411 return;
415 // if dead key, we search for our current offset char in the dead key
416 // offset table string comparison is needed
417 for (int32 i = 0; i < 32; i++) {
418 if (strncmp(&fChars[offset + 1], &fChars[deadKey[i] + 1], *numBytes)
419 == 0) {
420 *numBytes = fChars[deadKey[i + 1]];
422 switch (*numBytes) {
423 case 0:
424 // Not mapped
425 *chars = NULL;
426 break;
427 default:
429 // 1-, 2-, 3-, or 4-byte UTF-8 character
430 char *str = *chars = new char[*numBytes + 1];
431 strncpy(str, &fChars[deadKey[i + 1] + 1], *numBytes);
432 str[*numBytes] = 0;
433 break;
436 return;
438 i++;
441 // if not found we return the current char mapped
442 *chars = new char[*numBytes + 1];
443 strncpy(*chars, &fChars[offset + 1], *numBytes);
444 (*chars)[*numBytes] = 0;
448 /*! Get a list of characters translated from a given character and
449 set of modifiers to another set of modifiers.
451 status_t
452 BKeymap::GetModifiedCharacters(const char* in, int32 inModifiers,
453 int32 outModifiers, BObjectList<const char>* _outList)
455 if (in == NULL || *in == '\0' || _outList == NULL)
456 return B_BAD_VALUE;
458 for(uint32 i = 0; i < 128; i++) {
459 int32 inOffset = Offset(i, inModifiers);
460 size_t sizeIn = fChars[inOffset++];
461 if (sizeIn == 0 || memcmp(in, fChars + inOffset, sizeIn) != 0) {
462 // this character isn't mapped or doesn't match
463 continue;
466 int32 outOffset = Offset(i, outModifiers);
467 size_t sizeOut = fChars[outOffset++];
468 char* out = (char*)malloc(sizeOut + 1);
469 if (out == NULL)
470 return B_NO_MEMORY;
472 memcpy(out, fChars + outOffset, sizeOut);
473 out[sizeOut] = '\0';
475 _outList->AddItem((const char*)out);
478 return B_OK;
482 bool
483 BKeymap::operator==(const BKeymap& other) const
485 return fCharsSize == other.fCharsSize
486 && !memcmp(&fKeys, &other.fKeys, sizeof(fKeys))
487 && !memcmp(fChars, other.fChars, fCharsSize);
491 bool
492 BKeymap::operator!=(const BKeymap& other) const
494 return !(*this == other);
498 BKeymap&
499 BKeymap::operator=(const BKeymap& other)
501 Unset();
503 fChars = new char[fCharsSize];
504 fCharsSize = other.fCharsSize;
505 memcpy(fChars, other.fChars, fCharsSize);
506 memcpy(&fKeys, &other.fKeys, sizeof(fKeys));
508 return *this;
512 int32
513 BKeymap::Offset(uint32 keyCode, uint32 modifiers, uint32* _table) const
515 int32 offset;
516 uint32 table;
518 if (keyCode >= 128)
519 return -1;
521 switch (modifiers & kModifierKeys) {
522 case B_SHIFT_KEY:
523 offset = fKeys.shift_map[keyCode];
524 table = B_SHIFT_TABLE;
525 break;
526 case B_CAPS_LOCK:
527 offset = fKeys.caps_map[keyCode];
528 table = B_CAPS_TABLE;
529 break;
530 case B_CAPS_LOCK | B_SHIFT_KEY:
531 offset = fKeys.caps_shift_map[keyCode];
532 table = B_CAPS_SHIFT_TABLE;
533 break;
534 case B_CONTROL_KEY:
535 offset = fKeys.control_map[keyCode];
536 table = B_CONTROL_TABLE;
537 break;
538 case B_OPTION_KEY:
539 offset = fKeys.option_map[keyCode];
540 table = B_OPTION_TABLE;
541 break;
542 case B_OPTION_KEY | B_SHIFT_KEY:
543 offset = fKeys.option_shift_map[keyCode];
544 table = B_OPTION_SHIFT_TABLE;
545 break;
546 case B_OPTION_KEY | B_CAPS_LOCK:
547 offset = fKeys.option_caps_map[keyCode];
548 table = B_OPTION_CAPS_TABLE;
549 break;
550 case B_OPTION_KEY | B_SHIFT_KEY | B_CAPS_LOCK:
551 offset = fKeys.option_caps_shift_map[keyCode];
552 table = B_OPTION_CAPS_SHIFT_TABLE;
553 break;
554 default:
555 offset = fKeys.normal_map[keyCode];
556 table = B_NORMAL_TABLE;
557 break;
560 if (_table != NULL)
561 *_table = table;
563 if (offset >= (int32)fCharsSize)
564 return -1;
566 return offset;
570 uint8
571 BKeymap::DeadKeyIndex(int32 offset) const
573 if (fChars == NULL || offset <= 0)
574 return 0;
576 uint32 numBytes = fChars[offset];
577 if (!numBytes || numBytes > 4)
578 return 0;
580 char chars[5];
581 strncpy(chars, &fChars[offset + 1], numBytes);
582 chars[numBytes] = 0;
584 const int32 deadOffsets[] = {
585 fKeys.acute_dead_key[1],
586 fKeys.grave_dead_key[1],
587 fKeys.circumflex_dead_key[1],
588 fKeys.dieresis_dead_key[1],
589 fKeys.tilde_dead_key[1]
592 uint8 result = 0;
593 for (int32 i = 0; i < 5; i++) {
594 if (offset == deadOffsets[i])
595 return i + 1;
597 uint32 deadNumBytes = fChars[deadOffsets[i]];
598 if (!deadNumBytes)
599 continue;
601 if (strncmp(chars, &fChars[deadOffsets[i] + 1], deadNumBytes) == 0)
602 return i + 1;
605 return result;