From 35cbb796aa85dadeb5451104c536a33acf1ebcfe Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 12 Sep 2018 00:05:32 +0200 Subject: [PATCH] efi_loader: support Unicode text input Up to now the EFI_TEXT_INPUT_PROTOCOL only supported ASCII characters. With the patch it can consume UTF-8 from the console. Currently only the serial console and the console can deliver UTF-8. Local consoles are restricted to ASCII. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/charset.h | 8 ++ lib/charset.c | 137 +++++++++++++++++++++++------------ lib/efi_loader/efi_console.c | 13 ++-- test/unicode_ut.c | 8 +- 4 files changed, 108 insertions(+), 58 deletions(-) diff --git a/include/charset.h b/include/charset.h index 686db5a1fe..4d45e246e5 100644 --- a/include/charset.h +++ b/include/charset.h @@ -13,6 +13,14 @@ #define MAX_UTF8_PER_UTF16 3 +/** + * console_read_unicode() - read Unicode code point from console + * + * @code: pointer to store Unicode code point + * Return: 0 = success + */ +int console_read_unicode(s32 *code); + /** * utf8_get() - get next UTF-8 code point from buffer * diff --git a/lib/charset.c b/lib/charset.c index 72c808ce64..0cede9b60b 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -5,6 +5,7 @@ * Copyright (c) 2017 Rob Clark */ +#include #include #include #include @@ -18,67 +19,107 @@ static struct capitalization_table capitalization_table[] = CP437_CAPITALIZATION_TABLE; #endif -s32 utf8_get(const char **src) +/** + * get_code() - read Unicode code point from UTF-8 stream + * + * @read_u8: - stream reader + * @src: - string buffer passed to stream reader, optional + * Return: - Unicode code point + */ +static int get_code(u8 (*read_u8)(void *data), void *data) { - s32 code = 0; - unsigned char c; + s32 ch = 0; - if (!src || !*src) - return -1; - if (!**src) + ch = read_u8(data); + if (!ch) return 0; - c = **src; - if (c >= 0x80) { - ++*src; - if (!**src) - return -1; - /* - * We do not expect a continuation byte (0x80 - 0xbf). - * 0x80 is coded as 0xc2 0x80, so we cannot have less then 0xc2 - * here. - * The highest code point is 0x10ffff which is coded as - * 0xf4 0x8f 0xbf 0xbf. So we cannot have a byte above 0xf4. - */ - if (c < 0xc2 || code > 0xf4) - return -1; - if (c >= 0xe0) { - if (c >= 0xf0) { + if (ch >= 0xc2 && ch <= 0xf4) { + int code = 0; + + if (ch >= 0xe0) { + if (ch >= 0xf0) { /* 0xf0 - 0xf4 */ - c &= 0x07; - code = c << 18; - c = **src; - ++*src; - if (!**src) - return -1; - if (c < 0x80 || c > 0xbf) - return -1; - c &= 0x3f; + ch &= 0x07; + code = ch << 18; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; + ch &= 0x3f; } else { /* 0xe0 - 0xef */ - c &= 0x0f; + ch &= 0x0f; } - code += c << 12; + code += ch << 12; if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000) - return -1; - c = **src; - ++*src; - if (!**src) - return -1; - if (c < 0x80 || c > 0xbf) - return -1; + goto error; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; } /* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */ - c &= 0x3f; - code += c << 6; - c = **src; - if (c < 0x80 || c > 0xbf) - return -1; - c &= 0x3f; + ch &= 0x3f; + code += ch << 6; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; + ch &= 0x3f; + ch += code; + } else if (ch >= 0x80) { + goto error; } - code += c; + return ch; +error: + return '?'; +} + +/** + * read_string() - read byte from character string + * + * @data: - pointer to string + * Return: - byte read + * + * The string pointer is incremented if it does not point to '\0'. + */ +static u8 read_string(void *data) + +{ + const char **src = (const char **)data; + u8 c; + + if (!src || !*src || !**src) + return 0; + c = **src; ++*src; - return code; + return c; +} + +/** + * read_console() - read byte from console + * + * @src - not used, needed to match interface + * Return: - byte read + */ +static u8 read_console(void *data) +{ + return getc(); +} + +int console_read_unicode(s32 *code) +{ + if (!tstc()) { + /* No input available */ + return 1; + } + + /* Read Unicode code */ + *code = get_code(read_console, NULL); + return 0; +} + +s32 utf8_get(const char **src) +{ + return get_code(read_string, src); } int utf8_put(s32 code, char **dst) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 3ca6fe536c..6af083984c 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -449,23 +449,24 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( struct efi_simple_text_input_protocol *this, struct efi_input_key *key) { + efi_status_t ret; struct efi_input_key pressed_key = { .scan_code = 0, .unicode_char = 0, }; - char ch; + s32 ch; EFI_ENTRY("%p, %p", this, key); /* We don't do interrupts, so check for timers cooperatively */ efi_timer_check(); - if (!tstc()) { - /* No key pressed */ + ret = console_read_unicode(&ch); + if (ret) return EFI_EXIT(EFI_NOT_READY); - } - - ch = getc(); + /* We do not support multi-word codes */ + if (ch >= 0x10000) + ch = '?'; if (ch == cESC) { /* * Xterm Control Sequences diff --git a/test/unicode_ut.c b/test/unicode_ut.c index b94b4a651f..b115d18afd 100644 --- a/test/unicode_ut.c +++ b/test/unicode_ut.c @@ -178,7 +178,7 @@ static int ut_utf8_utf16_strlen(struct unit_test_state *uts) /* illegal utf-8 sequences */ ut_asserteq(4, utf8_utf16_strlen(j1)); - ut_asserteq(5, utf8_utf16_strlen(j2)); + ut_asserteq(4, utf8_utf16_strlen(j2)); ut_asserteq(3, utf8_utf16_strlen(j3)); return 0; @@ -196,7 +196,7 @@ static int ut_utf8_utf16_strnlen(struct unit_test_state *uts) /* illegal utf-8 sequences */ ut_asserteq(4, utf8_utf16_strnlen(j1, 16)); - ut_asserteq(5, utf8_utf16_strnlen(j2, 16)); + ut_asserteq(4, utf8_utf16_strnlen(j2, 16)); ut_asserteq(3, utf8_utf16_strnlen(j3, 16)); return 0; @@ -255,8 +255,8 @@ static int ut_utf8_utf16_strcpy(struct unit_test_state *uts) pos = buf; utf8_utf16_strcpy(&pos, j2); - ut_asserteq(5, pos - buf); - ut_assert(!ut_u16_strcmp(buf, L"j2??l", SIZE_MAX)); + ut_asserteq(4, pos - buf); + ut_assert(!ut_u16_strcmp(buf, L"j2?l", SIZE_MAX)); pos = buf; utf8_utf16_strcpy(&pos, j3); -- 2.39.5