mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-10-31 22:26:58 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1313 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1313 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *  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
 | |
|  */
 | |
| 
 | |
| #include <grub/charset.h>
 | |
| #include <grub/mm.h>
 | |
| #include <grub/misc.h>
 | |
| #include <grub/unicode.h>
 | |
| #include <grub/term.h>
 | |
| #include <grub/normal.h>
 | |
| #include <grub/safemath.h>
 | |
| 
 | |
| #if HAVE_FONT_SOURCE
 | |
| #include "widthspec.h"
 | |
| #endif
 | |
| 
 | |
| /* Returns -2 if not enough space, -1 on invalid character.  */
 | |
| grub_ssize_t
 | |
| grub_encode_utf8_character (grub_uint8_t *dest, grub_uint8_t *destend,
 | |
| 			    grub_uint32_t code)
 | |
| {
 | |
|   if (dest >= destend)
 | |
|     return -2;
 | |
|   if (code <= 0x007F)
 | |
|     {
 | |
|       *dest++ = code;
 | |
|       return 1;
 | |
|     }
 | |
|   if (code <= 0x07FF)
 | |
|     {
 | |
|       if (dest + 1 >= destend)
 | |
| 	return -2;
 | |
|       *dest++ = (code >> 6) | 0xC0;
 | |
|       *dest++ = (code & 0x3F) | 0x80;
 | |
|       return 2;
 | |
|     }
 | |
|   if ((code >= 0xDC00 && code <= 0xDFFF)
 | |
|       || (code >= 0xD800 && code <= 0xDBFF))
 | |
|     {
 | |
|       /* No surrogates in UCS-4... */
 | |
|       return -1;
 | |
|     }
 | |
|   if (code < 0x10000)
 | |
|     {
 | |
|       if (dest + 2 >= destend)
 | |
| 	return -2;
 | |
|       *dest++ = (code >> 12) | 0xE0;
 | |
|       *dest++ = ((code >> 6) & 0x3F) | 0x80;
 | |
|       *dest++ = (code & 0x3F) | 0x80;
 | |
|       return 3;
 | |
|     }
 | |
|   {
 | |
|     if (dest + 3 >= destend)
 | |
|       return -2;
 | |
|     *dest++ = (code >> 18) | 0xF0;
 | |
|     *dest++ = ((code >> 12) & 0x3F) | 0x80;
 | |
|     *dest++ = ((code >> 6) & 0x3F) | 0x80;
 | |
|     *dest++ = (code & 0x3F) | 0x80;
 | |
|     return 4;
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| /* Convert UCS-4 to UTF-8.  */
 | |
| grub_size_t
 | |
| grub_ucs4_to_utf8 (const 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;
 | |
|   grub_uint8_t *dest0 = dest;
 | |
| 
 | |
|   while (size-- && dest < destend)
 | |
|     {
 | |
|       grub_uint32_t code = *src++;
 | |
|       grub_ssize_t s;
 | |
|       s = grub_encode_utf8_character (dest, destend, code);
 | |
|       if (s == -2)
 | |
| 	break;
 | |
|       if (s == -1)
 | |
| 	{
 | |
| 	  *dest++ = '?';
 | |
| 	  continue;
 | |
| 	}
 | |
|       dest += s;
 | |
|     }
 | |
|   *dest = 0;
 | |
|   return dest - dest0;
 | |
| }
 | |
| 
 | |
| /* Returns the number of bytes the string src would occupy is converted
 | |
|    to UTF-8, excluding trailing \0.  */
 | |
| grub_size_t
 | |
| grub_get_num_of_utf8_bytes (const grub_uint32_t *src, grub_size_t size)
 | |
| {
 | |
|   grub_size_t remaining;
 | |
|   const grub_uint32_t *ptr;
 | |
|   grub_size_t cnt = 0;
 | |
| 
 | |
|   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;
 | |
|     }
 | |
|   return cnt;
 | |
| }
 | |
| 
 | |
| /* Convert UCS-4 to UTF-8.  */
 | |
| char *
 | |
| grub_ucs4_to_utf8_alloc (const grub_uint32_t *src, grub_size_t size)
 | |
| {
 | |
|   grub_uint8_t *ret;
 | |
|   grub_size_t cnt = grub_get_num_of_utf8_bytes (src, size) + 1;
 | |
| 
 | |
|   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)
 | |
| {
 | |
|   int count = 0;
 | |
|   grub_uint32_t code = 0;
 | |
| 
 | |
|   while (srcsize)
 | |
|     {
 | |
|       if (srcsize != (grub_size_t)-1)
 | |
| 	srcsize--;
 | |
|       if (!grub_utf8_process (*src++, &code, &count))
 | |
| 	return 0;
 | |
|       if (count != 0)
 | |
| 	continue;
 | |
|       if (code == 0)
 | |
| 	return 1;
 | |
|       if (code > GRUB_UNICODE_LAST_VALID)
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| grub_ssize_t
 | |
| 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_calloc (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)
 | |
|     {
 | |
|       int was_count = count;
 | |
|       if (srcsize != (grub_size_t)-1)
 | |
| 	srcsize--;
 | |
|       if (!grub_utf8_process (*src++, &code, &count))
 | |
| 	{
 | |
| 	  code = '?';
 | |
| 	  count = 0;
 | |
| 	  /* Character c may be valid, don't eat it.  */
 | |
| 	  if (was_count)
 | |
| 	    src--;
 | |
| 	}
 | |
|       if (count != 0)
 | |
| 	continue;
 | |
|       if (code == 0)
 | |
| 	break;
 | |
|       *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->len; cur++)
 | |
|     for (i = cur->start; i < cur->start + (unsigned) cur->len
 | |
| 	   && 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->len; cur++)
 | |
|     for (i = cur->start; i < cur->start + (unsigned) cur->len
 | |
| 	   && 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->len; cur++)
 | |
|     if (cur->start <= c && c < cur->start + (unsigned) cur->len)
 | |
|       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->len; cur++)
 | |
|     if (cur->start <= c && c < cur->start + (unsigned) cur->len)
 | |
|       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->len; cur++)
 | |
|     if (cur->start <= c && c < cur->start + (unsigned) cur->len)
 | |
|       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->len; cur++)
 | |
| 	  for (i = cur->start; i < cur->start + (unsigned) cur->len
 | |
| 		 && 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->len; cur++)
 | |
|     if (cur->start <= c && c < cur->start + (unsigned) cur->len)
 | |
|       return cur->comb_type;
 | |
| 
 | |
|   return GRUB_UNICODE_COMB_NONE;
 | |
| }
 | |
| 
 | |
| #if HAVE_FONT_SOURCE
 | |
| 
 | |
| grub_size_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));
 | |
| 
 | |
|   if (inlen && grub_iscntrl (*in))
 | |
|     {
 | |
|       out->base = *in;
 | |
|       out->variant = 0;
 | |
|       out->attributes = 0;
 | |
|       out->ncomb = 0;
 | |
|       out->estimated_width = 1;
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   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;
 | |
| 	  grub_size_t sz;
 | |
| 
 | |
| 	  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;
 | |
| 
 | |
| 	  if (out->ncomb + 1 <= (int) ARRAY_SIZE (out->combining_inline))
 | |
| 	    n = out->combining_inline;
 | |
| 	  else if (out->ncomb > (int) ARRAY_SIZE (out->combining_inline))
 | |
| 	    {
 | |
| 	      if (grub_add (out->ncomb, 1, &sz) ||
 | |
| 		  grub_mul (sz, sizeof (n[0]), &sz))
 | |
| 		goto fail;
 | |
| 
 | |
| 	      n = grub_realloc (out->combining_ptr, sz);
 | |
| 	      if (!n)
 | |
| 		{
 | |
|  fail:
 | |
| 		  grub_errno = GRUB_ERR_NONE;
 | |
| 		  continue;
 | |
| 		}
 | |
| 	      out->combining_ptr = n;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      n = grub_calloc (out->ncomb + 1, sizeof (n[0]));
 | |
| 	      if (!n)
 | |
| 		{
 | |
| 		  grub_errno = GRUB_ERR_NONE;
 | |
| 		  continue;
 | |
| 		}
 | |
| 	      grub_memcpy (n, out->combining_inline,
 | |
| 			   sizeof (out->combining_inline));
 | |
| 	      out->combining_ptr = n;
 | |
| 	    }
 | |
| 
 | |
| 	  for (j = last_comb_pointer; j < out->ncomb; j++)
 | |
| 	    if (is_type_after (n[j].type, comb_type))
 | |
| 	      break;
 | |
| 	  grub_memmove (n + j + 1,
 | |
| 			n + j,
 | |
| 			(out->ncomb - j)
 | |
| 			* sizeof (n[0]));
 | |
| 	  n[j].code = *ptr;
 | |
| 	  n[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;
 | |
|     }
 | |
|   return ptr - in;
 | |
| }
 | |
| 
 | |
| static void
 | |
| revert (struct grub_unicode_glyph *visual,
 | |
| 	struct grub_term_pos *pos,
 | |
| 	unsigned start, unsigned end)
 | |
| {
 | |
|   struct grub_unicode_glyph t;
 | |
|   unsigned i;
 | |
|   int a = 0, b = 0;
 | |
|   if (pos)
 | |
|     {
 | |
|       a = pos[visual[start].orig_pos].x;
 | |
|       b = pos[visual[end].orig_pos].x;
 | |
|     }
 | |
|   for (i = 0; i < (end - start) / 2 + 1; i++)
 | |
|     {
 | |
|       t = visual[start + i];
 | |
|       visual[start + i] = visual[end - i];
 | |
|       visual[end - i] = t;
 | |
| 
 | |
|       if (pos)
 | |
| 	{
 | |
| 	  pos[visual[start + i].orig_pos].x = a + b - pos[visual[start + i].orig_pos].x;
 | |
| 	  pos[visual[end - i].orig_pos].x = a + b - pos[visual[end - i].orig_pos].x;
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| static grub_ssize_t
 | |
| bidi_line_wrap (struct grub_unicode_glyph *visual_out,
 | |
| 		struct grub_unicode_glyph *visual,
 | |
| 		grub_size_t visual_len,
 | |
| 		grub_size_t (*getcharwidth) (const struct grub_unicode_glyph *visual, void *getcharwidth_arg),
 | |
| 		void *getcharwidth_arg,
 | |
| 		grub_size_t maxwidth, grub_size_t startwidth,
 | |
| 		grub_uint32_t contchar,
 | |
| 		struct grub_term_pos *pos, int primitive_wrap,
 | |
| 		grub_size_t log_end)
 | |
| {
 | |
|   struct grub_unicode_glyph *outptr = visual_out;
 | |
|   unsigned line_start = 0;
 | |
|   grub_ssize_t line_width;
 | |
|   unsigned k;
 | |
|   grub_ssize_t last_space = -1;
 | |
|   grub_ssize_t last_space_width = 0;
 | |
|   int lines = 0;
 | |
| 
 | |
|   if (!visual_len)
 | |
|     return 0;
 | |
| 
 | |
|   if (startwidth >= maxwidth && (grub_ssize_t) maxwidth > 0)
 | |
|     {
 | |
|       if (contchar)
 | |
| 	{
 | |
| 	  grub_memset (outptr, 0, sizeof (visual[0]));
 | |
| 	  outptr->base = contchar;
 | |
| 	  outptr++;
 | |
| 	}
 | |
|       grub_memset (outptr, 0, sizeof (visual[0]));
 | |
|       outptr->base = '\n';
 | |
|       outptr++;
 | |
|       startwidth = 0;
 | |
|     }
 | |
| 
 | |
|   line_width = startwidth;
 | |
| 
 | |
|   for (k = 0; k <= visual_len; k++)
 | |
|     {
 | |
|       grub_ssize_t last_width = 0;
 | |
|  
 | |
|       if (pos && k != visual_len)
 | |
| 	{
 | |
| 	  pos[visual[k].orig_pos].x = line_width;
 | |
| 	  pos[visual[k].orig_pos].y = lines;
 | |
| 	  pos[visual[k].orig_pos].valid = 1;
 | |
| 	}
 | |
| 
 | |
|       if (k == visual_len && pos)
 | |
| 	{
 | |
| 	  pos[log_end].x = line_width;
 | |
| 	  pos[log_end].y = lines;
 | |
| 	  pos[log_end].valid = 1;
 | |
| 	}
 | |
| 
 | |
|       if (getcharwidth && k != visual_len)
 | |
| 	line_width += last_width = getcharwidth (&visual[k], getcharwidth_arg);
 | |
| 
 | |
|       if (k != visual_len && (visual[k].base == ' '
 | |
| 			      || visual[k].base == '\t')
 | |
| 	  && !primitive_wrap)
 | |
| 	{
 | |
| 	  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;
 | |
| 	  unsigned kk = k;
 | |
| 
 | |
| 	  lines++;
 | |
| 
 | |
| 	  if (k != visual_len && last_space > (signed) line_start)
 | |
| 	    {
 | |
| 	      kk = last_space;
 | |
| 	      line_width -= last_space_width;
 | |
| 	    }
 | |
| 	  else if (k != visual_len && line_start == 0 && startwidth != 0
 | |
| 		   && !primitive_wrap && lines == 1
 | |
| 		   && line_width - startwidth < maxwidth)
 | |
| 	    {
 | |
| 	      kk = 0;
 | |
| 	      line_width -= startwidth;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    line_width = last_width;
 | |
| 
 | |
| 	  {
 | |
| 	    unsigned i;
 | |
| 	    for (i = line_start; i < kk; i++)
 | |
| 	      {
 | |
| 		if (visual[i].bidi_level > max_level)
 | |
| 		  max_level = visual[i].bidi_level;
 | |
| 		if (visual[i].bidi_level < min_odd_level && (visual[i].bidi_level & 1))
 | |
| 		  min_odd_level = visual[i].bidi_level;
 | |
| 	      }
 | |
| 	  }
 | |
| 
 | |
| 	  {
 | |
| 	    unsigned j;	  
 | |
| 	    /* FIXME: can be optimized.  */
 | |
| 	    for (j = max_level; j > min_odd_level - 1; j--)
 | |
| 	      {
 | |
| 		unsigned in = line_start;
 | |
| 		unsigned i;
 | |
| 		for (i = line_start; i < kk; i++)
 | |
| 		  {
 | |
| 		    if (i != line_start && visual[i].bidi_level >= j
 | |
| 			&& visual[i-1].bidi_level < j)
 | |
| 		      in = i;
 | |
| 		    if (visual[i].bidi_level >= j && (i + 1 == kk
 | |
| 						 || visual[i+1].bidi_level < j))
 | |
| 		      revert (visual, pos, in, i);
 | |
| 		  }
 | |
| 	      }
 | |
| 	  }
 | |
| 	  
 | |
| 	  {
 | |
| 	    unsigned i;
 | |
| 	    for (i = line_start; i < kk; i++)
 | |
| 	      {
 | |
| 		if (is_mirrored (visual[i].base) && visual[i].bidi_level)
 | |
| 		  visual[i].attributes |= GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR;
 | |
| 		if ((visual[i].attributes & GRUB_UNICODE_GLYPH_ATTRIBUTES_JOIN)
 | |
| 		    && visual[i].bidi_level)
 | |
| 		  {
 | |
| 		    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 < kk; 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 = kk - 1; i >= 0 && (unsigned) i + 1 > 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],
 | |
| 		       (kk - line_start) * sizeof (visual[0]));
 | |
| 	  outptr += kk - line_start;
 | |
| 	  if (kk != visual_len)
 | |
| 	    {
 | |
| 	      if (contchar)
 | |
| 		{
 | |
| 		  grub_memset (outptr, 0, sizeof (visual[0]));
 | |
| 		  outptr->base = contchar;
 | |
| 		  outptr++;
 | |
| 		}
 | |
| 	      grub_memset (outptr, 0, sizeof (visual[0]));
 | |
| 	      outptr->base = '\n';
 | |
| 	      outptr++;
 | |
| 	    }
 | |
| 
 | |
| 	  if ((signed) kk == last_space)
 | |
| 	    kk++;
 | |
| 
 | |
| 	  line_start = kk;
 | |
| 	  if (pos && kk != visual_len)
 | |
| 	    {
 | |
| 	      pos[visual[kk].orig_pos].x = 0;
 | |
| 	      pos[visual[kk].orig_pos].y = lines;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   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_size_t (*getcharwidth) (const struct grub_unicode_glyph *visual, void *getcharwidth_arg),
 | |
| 				  void *getcharwidth_arg,
 | |
| 				  grub_size_t maxwidth, grub_size_t startwidth,
 | |
| 				  grub_uint32_t contchar,
 | |
| 				  struct grub_term_pos *pos,
 | |
| 				  int primitive_wrap,
 | |
| 				  grub_size_t log_end)
 | |
| {
 | |
|   enum grub_bidi_type type = GRUB_BIDI_TYPE_L;
 | |
|   enum override_status {OVERRIDE_NEUTRAL = 0, OVERRIDE_R, OVERRIDE_L};
 | |
|   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;
 | |
| 
 | |
| #define push_stack(new_override, new_level)		\
 | |
|   {							\
 | |
|     if (new_level > GRUB_BIDI_MAX_EXPLICIT_LEVEL)	\
 | |
|       {							\
 | |
|       invalid_pushes++;					\
 | |
|       }							\
 | |
|     else						\
 | |
|       {							\
 | |
|       stack_level[stack_depth] = cur_level;		\
 | |
|       stack_override[stack_depth] = cur_override;	\
 | |
|       stack_depth++;					\
 | |
|       cur_level = new_level;				\
 | |
|       cur_override = new_override;			\
 | |
|       }							\
 | |
|   }
 | |
| 
 | |
| #define pop_stack()				\
 | |
|   {						\
 | |
|     if (invalid_pushes)				\
 | |
|       {						\
 | |
|       invalid_pushes--;				\
 | |
|       }						\
 | |
|     else if (stack_depth)			\
 | |
|       {						\
 | |
|       stack_depth--;				\
 | |
|       cur_level = stack_level[stack_depth];	\
 | |
|       cur_override = stack_override[stack_depth];	\
 | |
|       }							\
 | |
|   }
 | |
| 
 | |
|   visual = grub_calloc (logical_len, sizeof (visual[0]));
 | |
|   if (!visual)
 | |
|     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;
 | |
| 	  }
 | |
| 
 | |
| 	/* The tags: deprecated, never used.  */
 | |
| 	if (*lptr >= GRUB_UNICODE_TAG_START && *lptr <= GRUB_UNICODE_TAG_END)
 | |
| 	  continue;
 | |
| 
 | |
| 	p = grub_unicode_aglomerate_comb (lptr, logical + logical_len - lptr, 
 | |
| 					  &visual[visual_len]);
 | |
| 	visual[visual_len].orig_pos = lptr - logical;
 | |
| 	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;
 | |
| 	    /* Fallthrough.  */
 | |
| 	  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;
 | |
| 
 | |
| 	      visual[visual_len].bidi_level = cur_level;
 | |
| 	      if (cur_override != OVERRIDE_NEUTRAL)
 | |
| 		visual[visual_len].bidi_type = 
 | |
| 		  (cur_override == OVERRIDE_L) ? GRUB_BIDI_TYPE_L
 | |
| 		  : GRUB_BIDI_TYPE_R;
 | |
| 	      else
 | |
| 		visual[visual_len].bidi_type = 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 &&
 | |
| 		 visual[run_end].bidi_level == visual[run_start].bidi_level; run_end++);
 | |
| 	  if (run_start == 0)
 | |
| 	    prev_level = base_level;
 | |
| 	  else
 | |
| 	    prev_level = visual[run_start - 1].bidi_level;
 | |
| 	  if (run_end == visual_len)
 | |
| 	    next_level = base_level;
 | |
| 	  else
 | |
| 	    next_level = visual[run_end].bidi_level;
 | |
| 	  cur_run_level = visual[run_start].bidi_level;
 | |
| 	  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 (visual[i].bidi_type)
 | |
| 		{
 | |
| 		case GRUB_BIDI_TYPE_NSM:
 | |
| 		  visual[i].bidi_type = last_type;
 | |
| 		  break;
 | |
| 		case GRUB_BIDI_TYPE_EN:
 | |
| 		  if (last_strong_type == GRUB_BIDI_TYPE_AL)
 | |
| 		    visual[i].bidi_type = GRUB_BIDI_TYPE_AN;
 | |
| 		  break;
 | |
| 		case GRUB_BIDI_TYPE_L:
 | |
| 		case GRUB_BIDI_TYPE_R:
 | |
| 		  last_strong_type = visual[i].bidi_type;
 | |
| 		  break;
 | |
| 		case GRUB_BIDI_TYPE_ES:
 | |
| 		  if (last_type == GRUB_BIDI_TYPE_EN
 | |
| 		      && i + 1 < run_end 
 | |
| 		      && visual[i + 1].bidi_type == GRUB_BIDI_TYPE_EN)
 | |
| 		    visual[i].bidi_type = GRUB_BIDI_TYPE_EN;
 | |
| 		  else
 | |
| 		    visual[i].bidi_type = GRUB_BIDI_TYPE_ON;
 | |
| 		  break;
 | |
| 		case GRUB_BIDI_TYPE_ET:
 | |
| 		  {
 | |
| 		    unsigned j;
 | |
| 		    if (last_type == GRUB_BIDI_TYPE_EN)
 | |
| 		      {
 | |
| 			visual[i].bidi_type = GRUB_BIDI_TYPE_EN;
 | |
| 			break;
 | |
| 		      }
 | |
| 		    for (j = i; j < run_end
 | |
| 			   && visual[j].bidi_type == GRUB_BIDI_TYPE_ET; j++);
 | |
| 		    if (j != run_end && visual[j].bidi_type == GRUB_BIDI_TYPE_EN)
 | |
| 		      {
 | |
| 			for (; i < run_end
 | |
| 			       && visual[i].bidi_type == GRUB_BIDI_TYPE_ET; i++)
 | |
| 			  visual[i].bidi_type = GRUB_BIDI_TYPE_EN;
 | |
| 			i--;
 | |
| 			break;
 | |
| 		      }
 | |
| 		    for (; i < run_end
 | |
| 			   && visual[i].bidi_type == GRUB_BIDI_TYPE_ET; i++)
 | |
| 		      visual[i].bidi_type = GRUB_BIDI_TYPE_ON;
 | |
| 		    i--;
 | |
| 		    break;		
 | |
| 		  }
 | |
| 		  break;
 | |
| 		case GRUB_BIDI_TYPE_CS:
 | |
| 		  if (last_type == GRUB_BIDI_TYPE_EN
 | |
| 		      && i + 1 < run_end 
 | |
| 		      && visual[i + 1].bidi_type == GRUB_BIDI_TYPE_EN)
 | |
| 		    {
 | |
| 		      visual[i].bidi_type = GRUB_BIDI_TYPE_EN;
 | |
| 		      break;
 | |
| 		    }
 | |
| 		  if (last_type == GRUB_BIDI_TYPE_AN
 | |
| 		      && i + 1 < run_end 
 | |
| 		      && (visual[i + 1].bidi_type == GRUB_BIDI_TYPE_AN
 | |
| 			  || (visual[i + 1].bidi_type == GRUB_BIDI_TYPE_EN
 | |
| 			      && last_strong_type == GRUB_BIDI_TYPE_AL)))
 | |
| 		    {
 | |
| 		      visual[i].bidi_type = GRUB_BIDI_TYPE_EN;
 | |
| 		      break;
 | |
| 		    }
 | |
| 		  visual[i].bidi_type = GRUB_BIDI_TYPE_ON;
 | |
| 		  break;
 | |
| 		case GRUB_BIDI_TYPE_AL:
 | |
| 		  last_strong_type = visual[i].bidi_type;
 | |
| 		  visual[i].bidi_type = GRUB_BIDI_TYPE_R;
 | |
| 		  break;
 | |
| 		default: /* Make GCC happy.  */
 | |
| 		  break;
 | |
| 		}
 | |
| 	      last_type = visual[i].bidi_type;
 | |
| 	      if (visual[i].bidi_type == GRUB_BIDI_TYPE_EN
 | |
| 		  && last_strong_type == GRUB_BIDI_TYPE_L)
 | |
| 		visual[i].bidi_type = 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 &&
 | |
| 		     (visual[j].bidi_type == GRUB_BIDI_TYPE_B
 | |
| 		      || visual[j].bidi_type == GRUB_BIDI_TYPE_S
 | |
| 		      || visual[j].bidi_type == GRUB_BIDI_TYPE_WS
 | |
| 		      || visual[j].bidi_type == GRUB_BIDI_TYPE_ON); j++);
 | |
| 	      if (j == i)
 | |
| 		{
 | |
| 		  if (visual[i].bidi_type == 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 (visual[j].bidi_type == 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++)
 | |
| 		  visual[i].bidi_type = last_type;
 | |
| 	      else
 | |
| 		for (; i < j; i++)
 | |
| 		  visual[i].bidi_type = (cur_run_level & 1) ? GRUB_BIDI_TYPE_R
 | |
| 		    : GRUB_BIDI_TYPE_L;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       for (i = 0; i < visual_len; i++)
 | |
| 	{
 | |
| 	  if (!(visual[i].bidi_level & 1) && visual[i].bidi_type == GRUB_BIDI_TYPE_R)
 | |
| 	    {
 | |
| 	      visual[i].bidi_level++;
 | |
| 	      continue;
 | |
| 	    }
 | |
| 	  if (!(visual[i].bidi_level & 1) && (visual[i].bidi_type == GRUB_BIDI_TYPE_AN
 | |
| 				   || visual[i].bidi_type == GRUB_BIDI_TYPE_EN))
 | |
| 	    {
 | |
| 	      visual[i].bidi_level += 2;
 | |
| 	      continue;
 | |
| 	    }
 | |
| 	  if ((visual[i].bidi_level & 1) && (visual[i].bidi_type == GRUB_BIDI_TYPE_L
 | |
| 				  || visual[i].bidi_type == GRUB_BIDI_TYPE_AN
 | |
| 				  || visual[i].bidi_type == GRUB_BIDI_TYPE_EN))
 | |
| 	    {
 | |
| 	      visual[i].bidi_level++;
 | |
| 	      continue;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       for (i = 0; i < visual_len; i++)
 | |
| 	visual[i].bidi_level = 0;
 | |
|     }
 | |
| 
 | |
|   {
 | |
|     grub_ssize_t ret;
 | |
|     ret = bidi_line_wrap (visual_out, visual, visual_len,
 | |
| 			  getcharwidth, getcharwidth_arg, maxwidth, startwidth, contchar,
 | |
| 			  pos, primitive_wrap, log_end);
 | |
|     grub_free (visual);
 | |
|     return ret;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static int
 | |
| is_visible (const struct grub_unicode_glyph *gl)
 | |
| {
 | |
|   if (gl->ncomb)
 | |
|     return 1;
 | |
|   if (gl->base == GRUB_UNICODE_LRM || gl->base == GRUB_UNICODE_RLM)
 | |
|     return 0;
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 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_size_t (*getcharwidth) (const struct grub_unicode_glyph *visual, void *getcharwidth_arg),
 | |
| 			     void *getcharwidth_arg,
 | |
| 			     grub_size_t max_length, grub_size_t startwidth,
 | |
| 			     grub_uint32_t contchar, struct grub_term_pos *pos, int primitive_wrap)
 | |
| {
 | |
|   const grub_uint32_t *line_start = logical, *ptr;
 | |
|   struct grub_unicode_glyph *visual_ptr;
 | |
|   *visual_out = visual_ptr = grub_calloc (logical_len + 2,
 | |
| 					  3 * sizeof (visual_ptr[0]));
 | |
|   if (!visual_ptr)
 | |
|     return -1;
 | |
|   for (ptr = logical; ptr <= logical + logical_len; ptr++)
 | |
|     {
 | |
|       if (ptr == logical + logical_len || *ptr == '\n')
 | |
| 	{
 | |
| 	  grub_ssize_t ret;
 | |
| 	  grub_ssize_t i, j;
 | |
| 	  ret = grub_bidi_line_logical_to_visual (line_start,
 | |
| 						  ptr - line_start,
 | |
| 						  visual_ptr,
 | |
| 						  getcharwidth,
 | |
| 						  getcharwidth_arg,
 | |
| 						  max_length,
 | |
| 						  startwidth,
 | |
| 						  contchar,
 | |
| 						  pos,
 | |
| 						  primitive_wrap,
 | |
| 						  logical_len);
 | |
| 	  startwidth = 0;
 | |
| 
 | |
| 	  if (ret < 0)
 | |
| 	    {
 | |
| 	      grub_free (*visual_out);
 | |
| 	      return ret;
 | |
| 	    }
 | |
| 	  for (i = 0, j = 0; i < ret; i++)
 | |
| 	    if (is_visible(&visual_ptr[i]))
 | |
| 	      visual_ptr[j++] = visual_ptr[i];
 | |
| 	  visual_ptr += j;
 | |
| 	  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;
 | |
| }
 | |
| 
 | |
| const grub_uint32_t *
 | |
| grub_unicode_get_comb_start (const grub_uint32_t *str, 
 | |
| 			     const grub_uint32_t *cur)
 | |
| {
 | |
|   const grub_uint32_t *ptr;
 | |
|   for (ptr = cur; ptr >= str; ptr--)
 | |
|     {
 | |
|       if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_1
 | |
| 	  && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_16)
 | |
| 	continue;
 | |
| 
 | |
|       if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_17
 | |
| 	  && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_256)
 | |
| 	continue;
 | |
| 	
 | |
|       enum grub_comb_type comb_type;
 | |
|       comb_type = grub_unicode_get_comb_type (*ptr);
 | |
|       if (comb_type)
 | |
| 	continue;
 | |
|       return ptr;
 | |
|     }
 | |
|   return str;
 | |
| }
 | |
| 
 | |
| const grub_uint32_t *
 | |
| grub_unicode_get_comb_end (const grub_uint32_t *end, 
 | |
| 			   const grub_uint32_t *cur)
 | |
| {
 | |
|   const grub_uint32_t *ptr;
 | |
|   for (ptr = cur; ptr < end; ptr++)
 | |
|     {
 | |
|       if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_1
 | |
| 	  && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_16)
 | |
| 	continue;
 | |
| 
 | |
|       if (*ptr >= GRUB_UNICODE_VARIATION_SELECTOR_17
 | |
| 	  && *ptr <= GRUB_UNICODE_VARIATION_SELECTOR_256)
 | |
| 	continue;
 | |
| 	
 | |
|       enum grub_comb_type comb_type;
 | |
|       comb_type = grub_unicode_get_comb_type (*ptr);
 | |
|       if (comb_type)
 | |
| 	continue;
 | |
|       return ptr;
 | |
|     }
 | |
|   return end;
 | |
| }
 | |
| 
 | 
