diff --git a/include/grub/charset.h b/include/grub/charset.h index 6fdc2aa37..1d79d5d2c 100644 --- a/include/grub/charset.h +++ b/include/grub/charset.h @@ -117,7 +117,9 @@ grub_is_valid_utf8 (const grub_uint8_t *src, grub_size_t srcsize); int grub_utf8_to_ucs4_alloc (const char *msg, grub_uint32_t **unicode_msg, grub_uint32_t **last_position); - +void +grub_ucs4_to_utf8 (grub_uint32_t *src, grub_size_t size, + grub_uint8_t *dest, grub_size_t destsize); grub_size_t grub_utf8_to_ucs4 (grub_uint32_t *dest, grub_size_t destsize, const grub_uint8_t *src, grub_size_t srcsize, const grub_uint8_t **srcend); diff --git a/normal/charset.c b/normal/charset.c index db72a9026..20c47e2ba 100644 --- a/normal/charset.c +++ b/normal/charset.c @@ -120,6 +120,45 @@ grub_utf8_to_utf16 (grub_uint16_t *dest, grub_size_t destsize, return p - dest; } +/* Convert UCS-4 to UTF-8. */ +void +grub_ucs4_to_utf8 (grub_uint32_t *src, grub_size_t size, + grub_uint8_t *dest, grub_size_t destsize) +{ + /* Keep last char for \0. */ + grub_uint8_t *destend = dest + destsize - 1; + + while (size-- && dest < destend) + { + grub_uint32_t code = *src++; + + if (code <= 0x007F) + *dest++ = code; + else if (code <= 0x07FF) + { + if (dest + 1 >= destend) + break; + *dest++ = (code >> 6) | 0xC0; + *dest++ = (code & 0x3F) | 0x80; + } + else if ((code >= 0xDC00 && code <= 0xDFFF) + || (code >= 0xD800 && code <= 0xDBFF)) + { + /* No surrogates in UCS-4... */ + *dest++ = '?'; + } + else + { + if (dest + 2 >= destend) + break; + *dest++ = (code >> 12) | 0xE0; + *dest++ = ((code >> 6) & 0x3F) | 0x80; + *dest++ = (code & 0x3F) | 0x80; + } + } + *dest = 0; +} + /* Convert UCS-4 to UTF-8. */ char * grub_ucs4_to_utf8_alloc (grub_uint32_t *src, grub_size_t size) @@ -127,7 +166,7 @@ grub_ucs4_to_utf8_alloc (grub_uint32_t *src, grub_size_t size) grub_size_t remaining; grub_uint32_t *ptr; grub_size_t cnt = 0; - grub_uint8_t *ret, *dest; + grub_uint8_t *ret; remaining = size; ptr = src; @@ -152,34 +191,7 @@ grub_ucs4_to_utf8_alloc (grub_uint32_t *src, grub_size_t size) if (!ret) return 0; - dest = ret; - remaining = size; - ptr = src; - while (remaining--) - { - grub_uint32_t code = *ptr++; - - if (code <= 0x007F) - *dest++ = code; - else if (code <= 0x07FF) - { - *dest++ = (code >> 6) | 0xC0; - *dest++ = (code & 0x3F) | 0x80; - } - else if ((code >= 0xDC00 && code <= 0xDFFF) - || (code >= 0xD800 && code <= 0xDBFF)) - { - /* No surrogates in UCS-4... */ - *dest++ = '?'; - } - else - { - *dest++ = (code >> 12) | 0xE0; - *dest++ = ((code >> 6) & 0x3F) | 0x80; - *dest++ = (code & 0x3F) | 0x80; - } - } - *dest = 0; + grub_ucs4_to_utf8 (src, size, ret, cnt); return (char *) ret; } @@ -953,8 +965,9 @@ map_code (grub_uint32_t in, struct grub_term_output *term) if (in <= 0x7f) return in; - if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) == GRUB_TERM_CODE_TYPE_VGA) + switch (term->flags & GRUB_TERM_CODE_TYPE_MASK) { + case GRUB_TERM_CODE_TYPE_VGA: switch (in) { case GRUB_TERM_DISP_LEFT: @@ -979,9 +992,7 @@ map_code (grub_uint32_t in, struct grub_term_output *term) return 0xd9; } return '?'; - } - else - { + case GRUB_TERM_CODE_TYPE_ASCII: /* Better than nothing. */ switch (in) { @@ -990,7 +1001,7 @@ map_code (grub_uint32_t in, struct grub_term_output *term) case GRUB_TERM_DISP_UP: return '^'; - + case GRUB_TERM_DISP_RIGHT: return '>'; @@ -1017,8 +1028,7 @@ map_code (grub_uint32_t in, struct grub_term_output *term) /* Put a Unicode character. */ void -grub_putcode (grub_uint32_t code, - struct grub_term_output *term) +grub_putcode (grub_uint32_t code, struct grub_term_output *term) { struct grub_unicode_glyph c = { @@ -1042,9 +1052,25 @@ grub_putcode (grub_uint32_t code, return; } - c.base = map_code (code, term); + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UTF8_LOGICAL) + { + grub_uint8_t str[20], *ptr; + + grub_ucs4_to_utf8 (&code, 1, str, sizeof (str)); + + for (ptr = str; *ptr; ptr++) + { + c.base = *ptr; + (term->putchar) (&c); + } + } + else + { + c.base = map_code (code, term); + (term->putchar) (&c); + } - (term->putchar) (&c); if (code == '\n') grub_putcode ('\r', term); } diff --git a/term/serial.c b/term/serial.c index d98ca4c9e..14d1f4501 100644 --- a/term/serial.c +++ b/term/serial.c @@ -343,6 +343,8 @@ grub_serial_putchar (const struct grub_unicode_glyph *c) break; default: + if ((c->base & 0xC0) == 0xC0) + break; if (xpos >= TEXT_WIDTH) { xpos = 0;