efi_loader: support Unicode text input
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Tue, 11 Sep 2018 22:05:32 +0000 (00:05 +0200)
committerAlexander Graf <agraf@suse.de>
Sun, 23 Sep 2018 19:55:30 +0000 (21:55 +0200)
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 <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
include/charset.h
lib/charset.c
lib/efi_loader/efi_console.c
test/unicode_ut.c

index 686db5a1fe1665edf509018428932fcdc86f372c..4d45e246e5158fdb6d7c8acf6133d42c4a8ce0e2 100644 (file)
 
 #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
  *
index 72c808ce641563761e8d0d3d182b67af3411201c..0cede9b60b4ccdc0bc9cc28f4c3cbee0b9d77dc6 100644 (file)
@@ -5,6 +5,7 @@
  *  Copyright (c) 2017 Rob Clark
  */
 
+#include <common.h>
 #include <charset.h>
 #include <capitalization.h>
 #include <malloc.h>
@@ -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)
index 3ca6fe536cb85d5425cc3427d1d19ed797ab5931..6af083984c30655ef73790bb192124ce6ed227f9 100644 (file)
@@ -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
index b94b4a651fe6feca2b5ff12a2eefdc1a08ad0342..b115d18afd373cfbef584a1f4792dc9c27237910 100644 (file)
@@ -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);