diff options
Diffstat (limited to 'grub-core/normal/charset.c')
-rw-r--r-- | grub-core/normal/charset.c | 1274 |
1 files changed, 1274 insertions, 0 deletions
diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c new file mode 100644 index 0000000..85ead53 --- /dev/null +++ b/grub-core/normal/charset.c @@ -0,0 +1,1274 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + Current problems with Unicode rendering: + - B and BN bidi type characters (ignored) + - Mc type characters with combining class 0 (poorly combined) + - Mn type characters with combining class 0 (poorly combined) + - Me type characters with combining class 0 (poorly combined) + - Cf type characters (ignored) + - Cc type characters (ignored) + - Line-breaking rules (e.g. Zs type characters) + - Indic languages + - non-Semitic shaping (rarely used) + - Zl and Zp characters + - Combining characters of types 7, 8, 9, 21, 35, 36, 84, 91, 103, 107, + 118, 122, 129, 130, 132, 218, 224, 226, 233, 234 + - Private use characters (not really a problem) + - Variations (no font support) + - Vertical text + - Ligatures + Font information ignored: + - Kerning + - Justification data + - Glyph posititioning + - Baseline data + Most underline diacritics aren't displayed in gfxterm + */ + +/* Convert a (possibly null-terminated) UTF-8 string of at most SRCSIZE + bytes (if SRCSIZE is -1, it is ignored) in length to a UTF-16 string. + Return the number of characters converted. DEST must be able to hold + at least DESTSIZE characters. If an invalid sequence is found, return -1. + If SRCEND is not NULL, then *SRCEND is set to the next byte after the + last byte used in SRC. */ + +#include <grub/charset.h> +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/unicode.h> +#include <grub/term.h> +#include <grub/normal.h> + +#ifdef HAVE_UNIFONT_WIDTHSPEC +#include "widthspec.h" +#endif + +grub_ssize_t +grub_utf8_to_utf16 (grub_uint16_t *dest, grub_size_t destsize, + const grub_uint8_t *src, grub_size_t srcsize, + const grub_uint8_t **srcend) +{ + grub_uint16_t *p = dest; + int count = 0; + grub_uint32_t code = 0; + + if (srcend) + *srcend = src; + + while (srcsize && destsize) + { + grub_uint32_t c = *src++; + if (srcsize != (grub_size_t)-1) + srcsize--; + if (count) + { + if ((c & GRUB_UINT8_2_LEADINGBITS) != GRUB_UINT8_1_LEADINGBIT) + { + /* invalid */ + return -1; + } + else + { + code <<= 6; + code |= (c & GRUB_UINT8_6_TRAILINGBITS); + count--; + } + } + else + { + if (c == 0) + break; + + if ((c & GRUB_UINT8_1_LEADINGBIT) == 0) + code = c; + else if ((c & GRUB_UINT8_3_LEADINGBITS) == GRUB_UINT8_2_LEADINGBITS) + { + count = 1; + code = c & GRUB_UINT8_5_TRAILINGBITS; + } + else if ((c & GRUB_UINT8_4_LEADINGBITS) == GRUB_UINT8_3_LEADINGBITS) + { + count = 2; + code = c & GRUB_UINT8_4_TRAILINGBITS; + } + else if ((c & GRUB_UINT8_5_LEADINGBITS) == GRUB_UINT8_4_LEADINGBITS) + { + count = 3; + code = c & GRUB_UINT8_3_TRAILINGBITS; + } + else + return -1; + } + + if (count == 0) + { + if (destsize < 2 && code >= GRUB_UCS2_LIMIT) + break; + if (code >= GRUB_UCS2_LIMIT) + { + *p++ = GRUB_UTF16_UPPER_SURROGATE (code); + *p++ = GRUB_UTF16_LOWER_SURROGATE (code); + destsize -= 2; + } + else + { + *p++ = code; + destsize--; + } + } + } + + if (srcend) + *srcend = src; + 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 (code < 0x10000) + { + if (dest + 2 >= destend) + break; + *dest++ = (code >> 12) | 0xE0; + *dest++ = ((code >> 6) & 0x3F) | 0x80; + *dest++ = (code & 0x3F) | 0x80; + } + else + { + if (dest + 3 >= destend) + break; + *dest++ = (code >> 18) | 0xF0; + *dest++ = ((code >> 12) & 0x3F) | 0x80; + *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) +{ + grub_size_t remaining; + grub_uint32_t *ptr; + grub_size_t cnt = 0; + grub_uint8_t *ret; + + remaining = size; + ptr = src; + while (remaining--) + { + grub_uint32_t code = *ptr++; + + if (code <= 0x007F) + cnt++; + else if (code <= 0x07FF) + cnt += 2; + else if ((code >= 0xDC00 && code <= 0xDFFF) + || (code >= 0xD800 && code <= 0xDBFF)) + /* No surrogates in UCS-4... */ + cnt++; + else if (code < 0x10000) + cnt += 3; + else + cnt += 4; + } + cnt++; + + ret = grub_malloc (cnt); + if (!ret) + return 0; + + grub_ucs4_to_utf8 (src, size, ret, cnt); + + return (char *) ret; +} + +int +grub_is_valid_utf8 (const grub_uint8_t *src, grub_size_t srcsize) +{ + grub_uint32_t code = 0; + int count = 0; + + while (srcsize) + { + grub_uint32_t c = *src++; + if (srcsize != (grub_size_t)-1) + srcsize--; + if (count) + { + if ((c & 0xc0) != 0x80) + { + /* invalid */ + return 0; + } + else + { + code <<= 6; + code |= (c & 0x3f); + count--; + } + } + else + { + if (c == 0) + break; + + if ((c & 0x80) == 0x00) + code = c; + else if ((c & 0xe0) == 0xc0) + { + count = 1; + code = c & 0x1f; + } + else if ((c & 0xf0) == 0xe0) + { + count = 2; + code = c & 0x0f; + } + else if ((c & 0xf8) == 0xf0) + { + count = 3; + code = c & 0x07; + } + else + return 0; + } + } + + return 1; +} + +int +grub_utf8_to_ucs4_alloc (const char *msg, grub_uint32_t **unicode_msg, + grub_uint32_t **last_position) +{ + grub_size_t msg_len = grub_strlen (msg); + + *unicode_msg = grub_malloc (msg_len * sizeof (grub_uint32_t)); + + if (!*unicode_msg) + return -1; + + msg_len = grub_utf8_to_ucs4 (*unicode_msg, msg_len, + (grub_uint8_t *) msg, -1, 0); + + if (last_position) + *last_position = *unicode_msg + msg_len; + + return msg_len; +} + +/* Convert a (possibly null-terminated) UTF-8 string of at most SRCSIZE + bytes (if SRCSIZE is -1, it is ignored) in length to a UCS-4 string. + Return the number of characters converted. DEST must be able to hold + at least DESTSIZE characters. + If SRCEND is not NULL, then *SRCEND is set to the next byte after the + last byte used in SRC. */ +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) +{ + grub_uint32_t *p = dest; + int count = 0; + grub_uint32_t code = 0; + + if (srcend) + *srcend = src; + + while (srcsize && destsize) + { + grub_uint32_t c = *src++; + if (srcsize != (grub_size_t)-1) + srcsize--; + if (count) + { + if ((c & 0xc0) != 0x80) + { + /* invalid */ + code = '?'; + /* Character c may be valid, don't eat it. */ + src--; + if (srcsize != (grub_size_t)-1) + srcsize++; + count = 0; + } + else + { + code <<= 6; + code |= (c & 0x3f); + count--; + } + } + else + { + if (c == 0) + break; + + if ((c & 0x80) == 0x00) + code = c; + else if ((c & 0xe0) == 0xc0) + { + count = 1; + code = c & 0x1f; + } + else if ((c & 0xf0) == 0xe0) + { + count = 2; + code = c & 0x0f; + } + else if ((c & 0xf8) == 0xf0) + { + count = 3; + code = c & 0x07; + } + else + { + /* invalid */ + code = '?'; + count = 0; + } + } + + if (count == 0) + { + *p++ = code; + destsize--; + } + } + + if (srcend) + *srcend = src; + return p - dest; +} + +static grub_uint8_t *join_types = NULL; + +static void +unpack_join (void) +{ + unsigned i; + struct grub_unicode_compact_range *cur; + + join_types = grub_zalloc (GRUB_UNICODE_MAX_CACHED_CHAR); + if (!join_types) + { + grub_errno = GRUB_ERR_NONE; + return; + } + for (cur = grub_unicode_compact; cur->end; cur++) + for (i = cur->start; i <= cur->end + && i < GRUB_UNICODE_MAX_CACHED_CHAR; i++) + join_types[i] = cur->join_type; +} + +static grub_uint8_t *bidi_types = NULL; + +static void +unpack_bidi (void) +{ + unsigned i; + struct grub_unicode_compact_range *cur; + + bidi_types = grub_zalloc (GRUB_UNICODE_MAX_CACHED_CHAR); + if (!bidi_types) + { + grub_errno = GRUB_ERR_NONE; + return; + } + for (cur = grub_unicode_compact; cur->end; cur++) + for (i = cur->start; i <= cur->end + && i < GRUB_UNICODE_MAX_CACHED_CHAR; i++) + if (cur->bidi_mirror) + bidi_types[i] = cur->bidi_type | 0x80; + else + bidi_types[i] = cur->bidi_type | 0x00; +} + +static inline enum grub_bidi_type +get_bidi_type (grub_uint32_t c) +{ + struct grub_unicode_compact_range *cur; + + if (!bidi_types) + unpack_bidi (); + + if (bidi_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) + return bidi_types[c] & 0x7f; + + for (cur = grub_unicode_compact; cur->end; cur++) + if (cur->start <= c && c <= cur->end) + return cur->bidi_type; + + return GRUB_BIDI_TYPE_L; +} + +static inline enum grub_join_type +get_join_type (grub_uint32_t c) +{ + struct grub_unicode_compact_range *cur; + + if (!join_types) + unpack_join (); + + if (join_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) + return join_types[c]; + + for (cur = grub_unicode_compact; cur->end; cur++) + if (cur->start <= c && c <= cur->end) + return cur->join_type; + + return GRUB_JOIN_TYPE_NONJOINING; +} + +static inline int +is_mirrored (grub_uint32_t c) +{ + struct grub_unicode_compact_range *cur; + + if (!bidi_types) + unpack_bidi (); + + if (bidi_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) + return !!(bidi_types[c] & 0x80); + + for (cur = grub_unicode_compact; cur->end; cur++) + if (cur->start <= c && c <= cur->end) + return cur->bidi_mirror; + + return 0; +} + +enum grub_comb_type +grub_unicode_get_comb_type (grub_uint32_t c) +{ + static grub_uint8_t *comb_types = NULL; + struct grub_unicode_compact_range *cur; + + if (!comb_types) + { + unsigned i; + comb_types = grub_zalloc (GRUB_UNICODE_MAX_CACHED_CHAR); + if (comb_types) + for (cur = grub_unicode_compact; cur->end; cur++) + for (i = cur->start; i <= cur->end + && i < GRUB_UNICODE_MAX_CACHED_CHAR; i++) + comb_types[i] = cur->comb_type; + else + grub_errno = GRUB_ERR_NONE; + } + + if (comb_types && c < GRUB_UNICODE_MAX_CACHED_CHAR) + return comb_types[c]; + + for (cur = grub_unicode_compact; cur->end; cur++) + if (cur->start <= c && c <= cur->end) + return cur->comb_type; + + return GRUB_UNICODE_COMB_NONE; +} + +#ifdef HAVE_UNIFONT_WIDTHSPEC + +grub_ssize_t +grub_unicode_estimate_width (const struct grub_unicode_glyph *c) +{ + if (grub_unicode_get_comb_type (c->base)) + return 0; + if (widthspec[c->base >> 3] & (1 << (c->base & 7))) + return 2; + else + return 1; +} + +#endif + +static inline int +is_type_after (enum grub_comb_type a, enum grub_comb_type b) +{ + /* Shadda is numerically higher than most of Arabic diacritics but has + to be rendered before them. */ + if (a == GRUB_UNICODE_COMB_ARABIC_SHADDA + && b <= GRUB_UNICODE_COMB_ARABIC_KASRA + && b >= GRUB_UNICODE_COMB_ARABIC_FATHATAN) + return 0; + if (b == GRUB_UNICODE_COMB_ARABIC_SHADDA + && a <= GRUB_UNICODE_COMB_ARABIC_KASRA + && a >= GRUB_UNICODE_COMB_ARABIC_FATHATAN) + return 1; + return a > b; +} + +grub_size_t +grub_unicode_aglomerate_comb (const grub_uint32_t *in, grub_size_t inlen, + struct grub_unicode_glyph *out) +{ + int haveout = 0; + const grub_uint32_t *ptr; + unsigned last_comb_pointer = 0; + + grub_memset (out, 0, sizeof (*out)); + + for (ptr = in; ptr < in + inlen; ptr++) + { + /* Variation selectors >= 17 are outside of BMP and SMP. + Handle variation selectors first to avoid potentially costly lookups. + */ + if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_1 + && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_16) + { + if (haveout) + out->variant = *ptr - GRUB_UNICODE_VARIATION_SELECTOR_1 + 1; + continue; + + } + if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_17 + && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_256) + { + if (haveout) + out->variant = *ptr - GRUB_UNICODE_VARIATION_SELECTOR_17 + 17; + continue; + } + + enum grub_comb_type comb_type; + comb_type = grub_unicode_get_comb_type (*ptr); + if (comb_type) + { + struct grub_unicode_combining *n; + unsigned j; + + if (!haveout) + continue; + + if (comb_type == GRUB_UNICODE_COMB_MC + || comb_type == GRUB_UNICODE_COMB_ME + || comb_type == GRUB_UNICODE_COMB_MN) + last_comb_pointer = out->ncomb; + n = grub_realloc (out->combining, + sizeof (n[0]) * (out->ncomb + 1)); + if (!n) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + out->combining = n; + + for (j = last_comb_pointer; j < out->ncomb; j++) + if (is_type_after (out->combining[j].type, comb_type)) + break; + grub_memmove (out->combining + j + 1, + out->combining + j, + (out->ncomb - j) + * sizeof (out->combining[0])); + out->combining = n; + out->combining[j].code = *ptr; + out->combining[j].type = comb_type; + out->ncomb++; + continue; + } + if (haveout) + return ptr - in; + haveout = 1; + out->base = *ptr; + out->variant = 0; + out->attributes = 0; + out->ncomb = 0; + out->estimated_width = 1; + out->combining = NULL; + } + return ptr - in; +} + +static grub_ssize_t +bidi_line_wrap (struct grub_unicode_glyph *visual_out, + struct grub_unicode_glyph *visual, + grub_size_t visual_len, unsigned *levels, + grub_ssize_t (*getcharwidth) (const struct grub_unicode_glyph *visual), + grub_size_t maxwidth, grub_size_t startwidth) +{ + struct grub_unicode_glyph *outptr = visual_out; + unsigned line_start = 0; + grub_ssize_t line_width = startwidth; + unsigned k; + grub_ssize_t last_space = -1; + grub_ssize_t last_space_width = 0; + + auto void revert (unsigned start, unsigned end); + void revert (unsigned start, unsigned end) + { + struct grub_unicode_glyph t; + unsigned i, tl; + for (i = 0; i <= (end - start) / 2; i++) + { + t = visual[start + i]; + visual[start + i] = visual[end - i]; + visual[end - i] = t; + tl = levels[start + i]; + levels[start + i] = levels[end - i]; + levels[end - i] = tl; + } + } + + if (!visual_len) + return 0; + + for (k = 0; k <= visual_len; k++) + { + grub_ssize_t last_width = 0; + + if (getcharwidth && k != visual_len) + line_width += last_width = getcharwidth (&visual[k]); + + if (k != visual_len && visual[k].base == ' ') + { + last_space = k; + last_space_width = line_width; + } + + if (((grub_ssize_t) maxwidth > 0 + && line_width > (grub_ssize_t) maxwidth) || k == visual_len) + { + unsigned min_odd_level = 0xffffffff; + unsigned max_level = 0; + + if (k != visual_len && last_space > (signed) line_start) + k = last_space; + else if (k != visual_len && line_start == 0 && startwidth != 0) + { + k = 0; + last_space_width = startwidth; + } + else + last_space_width = line_width - last_width; + + { + unsigned i; + for (i = line_start; i < k; i++) + { + if (levels[i] > max_level) + max_level = levels[i]; + if (levels[i] < min_odd_level && (levels[i] & 1)) + min_odd_level = levels[i]; + } + } + + { + unsigned j; + /* FIXME: can be optimized. */ + for (j = max_level; j >= min_odd_level; j--) + { + unsigned in = 0; + unsigned i; + for (i = line_start; i < k; i++) + { + if (i != line_start && levels[i] >= j && levels[i-1] < j) + in = i; + if (levels[i] >= j && (i + 1 == k || levels[i+1] < j)) + revert (in, i); + } + } + } + + { + unsigned i; + for (i = line_start; i < k; i++) + { + if (is_mirrored (visual[i].base) && levels[i]) + visual[i].attributes |= GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR; + if ((visual[i].attributes & GRUB_UNICODE_GLYPH_ATTRIBUTES_JOIN) + && levels[i]) + { + int left, right; + left = visual[i].attributes + & (GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED + | GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED_EXPLICIT); + right = visual[i].attributes + & (GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED + | GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED_EXPLICIT); + visual[i].attributes &= ~GRUB_UNICODE_GLYPH_ATTRIBUTES_JOIN; + left <<= GRUB_UNICODE_GLYPH_ATTRIBUTES_JOIN_LEFT_TO_RIGHT_SHIFT; + right >>= GRUB_UNICODE_GLYPH_ATTRIBUTES_JOIN_LEFT_TO_RIGHT_SHIFT; + visual[i].attributes |= (left | right); + } + } + } + + { + int left_join = 0; + unsigned i; + for (i = line_start; i < k; i++) + { + enum grub_join_type join_type = get_join_type (visual[i].base); + if (!(visual[i].attributes + & GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED_EXPLICIT) + && (join_type == GRUB_JOIN_TYPE_LEFT + || join_type == GRUB_JOIN_TYPE_DUAL)) + { + if (left_join) + visual[i].attributes + |= GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED; + else + visual[i].attributes + &= ~GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED; + } + if (join_type == GRUB_JOIN_TYPE_NONJOINING + || join_type == GRUB_JOIN_TYPE_LEFT) + left_join = 0; + if (join_type == GRUB_JOIN_TYPE_RIGHT + || join_type == GRUB_JOIN_TYPE_DUAL + || join_type == GRUB_JOIN_TYPE_CAUSING) + left_join = 1; + } + } + + { + int right_join = 0; + signed i; + for (i = k - 1; i >= (signed) line_start; i--) + { + enum grub_join_type join_type = get_join_type (visual[i].base); + if (!(visual[i].attributes + & GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED_EXPLICIT) + && (join_type == GRUB_JOIN_TYPE_RIGHT + || join_type == GRUB_JOIN_TYPE_DUAL)) + { + if (right_join) + visual[i].attributes + |= GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED; + else + visual[i].attributes + &= ~GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED; + } + if (join_type == GRUB_JOIN_TYPE_NONJOINING + || join_type == GRUB_JOIN_TYPE_RIGHT) + right_join = 0; + if (join_type == GRUB_JOIN_TYPE_LEFT + || join_type == GRUB_JOIN_TYPE_DUAL + || join_type == GRUB_JOIN_TYPE_CAUSING) + right_join = 1; + } + } + + grub_memcpy (outptr, &visual[line_start], + (k - line_start) * sizeof (visual[0])); + outptr += k - line_start; + if (k != visual_len) + { + grub_memset (outptr, 0, sizeof (visual[0])); + outptr->base = '\n'; + outptr++; + } + + if ((signed) k == last_space) + k++; + + line_start = k; + line_width -= last_space_width; + } + } + + return outptr - visual_out; +} + + +static grub_ssize_t +grub_bidi_line_logical_to_visual (const grub_uint32_t *logical, + grub_size_t logical_len, + struct grub_unicode_glyph *visual_out, + grub_ssize_t (*getcharwidth) (const struct grub_unicode_glyph *visual), + grub_size_t maxwidth, grub_size_t startwidth) +{ + enum grub_bidi_type type = GRUB_BIDI_TYPE_L; + enum override_status {OVERRIDE_NEUTRAL = 0, OVERRIDE_R, OVERRIDE_L}; + unsigned *levels; + enum grub_bidi_type *resolved_types; + unsigned base_level; + enum override_status cur_override; + unsigned i; + unsigned stack_level[GRUB_BIDI_MAX_EXPLICIT_LEVEL + 3]; + enum override_status stack_override[GRUB_BIDI_MAX_EXPLICIT_LEVEL + 3]; + unsigned stack_depth = 0; + unsigned invalid_pushes = 0; + unsigned visual_len = 0; + unsigned run_start, run_end; + struct grub_unicode_glyph *visual; + unsigned cur_level; + int bidi_needed = 0; + + auto void push_stack (unsigned new_override, unsigned new_level); + void push_stack (unsigned new_override, unsigned new_level) + { + if (new_level > GRUB_BIDI_MAX_EXPLICIT_LEVEL) + { + invalid_pushes++; + return; + } + stack_level[stack_depth] = cur_level; + stack_override[stack_depth] = cur_override; + stack_depth++; + cur_level = new_level; + cur_override = new_override; + } + + auto void pop_stack (void); + void pop_stack (void) + { + if (invalid_pushes) + { + invalid_pushes--; + return; + } + if (!stack_depth) + return; + stack_depth--; + cur_level = stack_level[stack_depth]; + cur_override = stack_override[stack_depth]; + } + + levels = grub_malloc (sizeof (levels[0]) * logical_len); + if (!levels) + return -1; + + resolved_types = grub_malloc (sizeof (resolved_types[0]) * logical_len); + if (!resolved_types) + { + grub_free (levels); + return -1; + } + + visual = grub_malloc (sizeof (visual[0]) * logical_len); + if (!visual) + { + grub_free (resolved_types); + grub_free (levels); + return -1; + } + + for (i = 0; i < logical_len; i++) + { + type = get_bidi_type (logical[i]); + if (type == GRUB_BIDI_TYPE_L || type == GRUB_BIDI_TYPE_AL + || type == GRUB_BIDI_TYPE_R) + break; + } + if (type == GRUB_BIDI_TYPE_R || type == GRUB_BIDI_TYPE_AL) + base_level = 1; + else + base_level = 0; + + cur_level = base_level; + cur_override = OVERRIDE_NEUTRAL; + { + const grub_uint32_t *lptr; + enum {JOIN_DEFAULT, NOJOIN, JOIN_FORCE} join_state = JOIN_DEFAULT; + int zwj_propagate_to_previous = 0; + for (lptr = logical; lptr < logical + logical_len;) + { + grub_size_t p; + + if (*lptr == GRUB_UNICODE_ZWJ) + { + if (zwj_propagate_to_previous) + { + visual[visual_len - 1].attributes + |= GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED_EXPLICIT + | GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED; + } + zwj_propagate_to_previous = 0; + join_state = JOIN_FORCE; + lptr++; + continue; + } + + if (*lptr == GRUB_UNICODE_ZWNJ) + { + if (zwj_propagate_to_previous) + { + visual[visual_len - 1].attributes + |= GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED_EXPLICIT; + visual[visual_len - 1].attributes + &= ~GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED; + } + zwj_propagate_to_previous = 0; + join_state = NOJOIN; + lptr++; + continue; + } + + p = grub_unicode_aglomerate_comb (lptr, logical + logical_len - lptr, + &visual[visual_len]); + + type = get_bidi_type (visual[visual_len].base); + switch (type) + { + case GRUB_BIDI_TYPE_RLE: + bidi_needed = 1; + push_stack (cur_override, (cur_level | 1) + 1); + break; + case GRUB_BIDI_TYPE_RLO: + bidi_needed = 1; + push_stack (OVERRIDE_R, (cur_level | 1) + 1); + break; + case GRUB_BIDI_TYPE_LRE: + push_stack (cur_override, (cur_level & ~1) + 2); + break; + case GRUB_BIDI_TYPE_LRO: + push_stack (OVERRIDE_L, (cur_level & ~1) + 2); + break; + case GRUB_BIDI_TYPE_PDF: + pop_stack (); + break; + case GRUB_BIDI_TYPE_BN: + break; + case GRUB_BIDI_TYPE_R: + case GRUB_BIDI_TYPE_AL: + bidi_needed = 1; + default: + { + if (join_state == JOIN_FORCE) + { + visual[visual_len].attributes + |= GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED_EXPLICIT + | GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED; + } + + if (join_state == NOJOIN) + { + visual[visual_len].attributes + |= GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED_EXPLICIT; + visual[visual_len].attributes + &= ~GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED; + } + + join_state = JOIN_DEFAULT; + zwj_propagate_to_previous = 1; + + levels[visual_len] = cur_level; + if (cur_override != OVERRIDE_NEUTRAL) + resolved_types[visual_len] = + (cur_override == OVERRIDE_L) ? GRUB_BIDI_TYPE_L + : GRUB_BIDI_TYPE_R; + else + resolved_types[visual_len] = type; + visual_len++; + } + } + lptr += p; + } + } + + if (bidi_needed) + { + for (run_start = 0; run_start < visual_len; run_start = run_end) + { + unsigned prev_level, next_level, cur_run_level; + unsigned last_type, last_strong_type; + for (run_end = run_start; run_end < visual_len && + levels[run_end] == levels[run_start]; run_end++); + if (run_start == 0) + prev_level = base_level; + else + prev_level = levels[run_start - 1]; + if (run_end == visual_len) + next_level = base_level; + else + next_level = levels[run_end]; + cur_run_level = levels[run_start]; + if (prev_level & 1) + last_type = GRUB_BIDI_TYPE_R; + else + last_type = GRUB_BIDI_TYPE_L; + last_strong_type = last_type; + for (i = run_start; i < run_end; i++) + { + switch (resolved_types[i]) + { + case GRUB_BIDI_TYPE_NSM: + resolved_types[i] = last_type; + break; + case GRUB_BIDI_TYPE_EN: + if (last_strong_type == GRUB_BIDI_TYPE_AL) + resolved_types[i] = GRUB_BIDI_TYPE_AN; + break; + case GRUB_BIDI_TYPE_L: + case GRUB_BIDI_TYPE_R: + last_strong_type = resolved_types[i]; + break; + case GRUB_BIDI_TYPE_ES: + if (last_type == GRUB_BIDI_TYPE_EN + && i + 1 < run_end + && resolved_types[i + 1] == GRUB_BIDI_TYPE_EN) + resolved_types[i] = GRUB_BIDI_TYPE_EN; + else + resolved_types[i] = GRUB_BIDI_TYPE_ON; + break; + case GRUB_BIDI_TYPE_ET: + { + unsigned j; + if (last_type == GRUB_BIDI_TYPE_EN) + { + resolved_types[i] = GRUB_BIDI_TYPE_EN; + break; + } + for (j = i; j < run_end + && resolved_types[j] == GRUB_BIDI_TYPE_ET; j++); + if (j != run_end && resolved_types[j] == GRUB_BIDI_TYPE_EN) + { + for (; i < run_end + && resolved_types[i] == GRUB_BIDI_TYPE_ET; i++) + resolved_types[i] = GRUB_BIDI_TYPE_EN; + i--; + break; + } + for (; i < run_end + && resolved_types[i] == GRUB_BIDI_TYPE_ET; i++) + resolved_types[i] = GRUB_BIDI_TYPE_ON; + i--; + break; + } + break; + case GRUB_BIDI_TYPE_CS: + if (last_type == GRUB_BIDI_TYPE_EN + && i + 1 < run_end + && resolved_types[i + 1] == GRUB_BIDI_TYPE_EN) + { + resolved_types[i] = GRUB_BIDI_TYPE_EN; + break; + } + if (last_type == GRUB_BIDI_TYPE_AN + && i + 1 < run_end + && (resolved_types[i + 1] == GRUB_BIDI_TYPE_AN + || (resolved_types[i + 1] == GRUB_BIDI_TYPE_EN + && last_strong_type == GRUB_BIDI_TYPE_AL))) + { + resolved_types[i] = GRUB_BIDI_TYPE_EN; + break; + } + resolved_types[i] = GRUB_BIDI_TYPE_ON; + break; + case GRUB_BIDI_TYPE_AL: + last_strong_type = resolved_types[i]; + resolved_types[i] = GRUB_BIDI_TYPE_R; + break; + default: /* Make GCC happy. */ + break; + } + last_type = resolved_types[i]; + if (resolved_types[i] == GRUB_BIDI_TYPE_EN + && last_strong_type == GRUB_BIDI_TYPE_L) + resolved_types[i] = GRUB_BIDI_TYPE_L; + } + if (prev_level & 1) + last_type = GRUB_BIDI_TYPE_R; + else + last_type = GRUB_BIDI_TYPE_L; + for (i = run_start; i < run_end; ) + { + unsigned j; + unsigned next_type; + for (j = i; j < run_end && + (resolved_types[j] == GRUB_BIDI_TYPE_B + || resolved_types[j] == GRUB_BIDI_TYPE_S + || resolved_types[j] == GRUB_BIDI_TYPE_WS + || resolved_types[j] == GRUB_BIDI_TYPE_ON); j++); + if (j == i) + { + if (resolved_types[i] == GRUB_BIDI_TYPE_L) + last_type = GRUB_BIDI_TYPE_L; + else + last_type = GRUB_BIDI_TYPE_R; + i++; + continue; + } + if (j == run_end) + next_type = (next_level & 1) ? GRUB_BIDI_TYPE_R : GRUB_BIDI_TYPE_L; + else + { + if (resolved_types[j] == GRUB_BIDI_TYPE_L) + next_type = GRUB_BIDI_TYPE_L; + else + next_type = GRUB_BIDI_TYPE_R; + } + if (next_type == last_type) + for (; i < j; i++) + resolved_types[i] = last_type; + else + for (; i < j; i++) + resolved_types[i] = (cur_run_level & 1) ? GRUB_BIDI_TYPE_R + : GRUB_BIDI_TYPE_L; + } + } + + for (i = 0; i < visual_len; i++) + { + if (!(levels[i] & 1) && resolved_types[i] == GRUB_BIDI_TYPE_R) + { + levels[i]++; + continue; + } + if (!(levels[i] & 1) && (resolved_types[i] == GRUB_BIDI_TYPE_AN + || resolved_types[i] == GRUB_BIDI_TYPE_EN)) + { + levels[i] += 2; + continue; + } + if ((levels[i] & 1) && (resolved_types[i] == GRUB_BIDI_TYPE_L + || resolved_types[i] == GRUB_BIDI_TYPE_AN + || resolved_types[i] == GRUB_BIDI_TYPE_EN)) + { + levels[i]++; + continue; + } + } + } + else + { + for (i = 0; i < visual_len; i++) + levels[i] = 0; + } + grub_free (resolved_types); + + { + grub_ssize_t ret; + ret = bidi_line_wrap (visual_out, visual, visual_len, levels, + getcharwidth, maxwidth, startwidth); + grub_free (levels); + grub_free (visual); + return ret; + } +} + +grub_ssize_t +grub_bidi_logical_to_visual (const grub_uint32_t *logical, + grub_size_t logical_len, + struct grub_unicode_glyph **visual_out, + grub_ssize_t (*getcharwidth) (const struct grub_unicode_glyph *visual), + grub_size_t max_length, grub_size_t startwidth) +{ + const grub_uint32_t *line_start = logical, *ptr; + struct grub_unicode_glyph *visual_ptr; + *visual_out = visual_ptr = grub_malloc (2 * sizeof (visual_ptr[0]) + * logical_len); + if (!visual_ptr) + return -1; + for (ptr = logical; ptr <= logical + logical_len; ptr++) + { + if (ptr == logical + logical_len || *ptr == '\n') + { + grub_ssize_t ret; + ret = grub_bidi_line_logical_to_visual (line_start, + ptr - line_start, + visual_ptr, + getcharwidth, + max_length, + startwidth); + startwidth = 0; + + if (ret < 0) + { + grub_free (*visual_out); + return ret; + } + visual_ptr += ret; + line_start = ptr; + if (ptr != logical + logical_len) + { + grub_memset (visual_ptr, 0, sizeof (visual_ptr[0])); + visual_ptr->base = '\n'; + visual_ptr++; + line_start++; + } + } + } + return visual_ptr - *visual_out; +} + +grub_uint32_t +grub_unicode_mirror_code (grub_uint32_t in) +{ + int i; + for (i = 0; grub_unicode_bidi_pairs[i].key; i++) + if (grub_unicode_bidi_pairs[i].key == in) + return grub_unicode_bidi_pairs[i].replace; + return in; +} + +grub_uint32_t +grub_unicode_shape_code (grub_uint32_t in, grub_uint8_t attr) +{ + int i; + if (!(in >= GRUB_UNICODE_ARABIC_START + && in < GRUB_UNICODE_ARABIC_END)) + return in; + + for (i = 0; grub_unicode_arabic_shapes[i].code; i++) + if (grub_unicode_arabic_shapes[i].code == in) + { + grub_uint32_t out = 0; + switch (attr & (GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED + | GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED)) + { + case 0: + out = grub_unicode_arabic_shapes[i].isolated; + break; + case GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED: + out = grub_unicode_arabic_shapes[i].right_linked; + break; + case GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED: + out = grub_unicode_arabic_shapes[i].left_linked; + break; + case GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED + |GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED: + out = grub_unicode_arabic_shapes[i].both_linked; + break; + } + if (out) + return out; + } + + return in; +} |