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 <xypron.glpk@gmx.de> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
		
							parent
							
								
									cce289a928
								
							
						
					
					
						commit
						35cbb796aa
					
				|  | @ -13,6 +13,14 @@ | ||||||
| 
 | 
 | ||||||
| #define MAX_UTF8_PER_UTF16 3 | #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 |  * utf8_get() - get next UTF-8 code point from buffer | ||||||
|  * |  * | ||||||
|  |  | ||||||
							
								
								
									
										137
									
								
								lib/charset.c
								
								
								
								
							
							
						
						
									
										137
									
								
								lib/charset.c
								
								
								
								
							|  | @ -5,6 +5,7 @@ | ||||||
|  *  Copyright (c) 2017 Rob Clark |  *  Copyright (c) 2017 Rob Clark | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include <common.h> | ||||||
| #include <charset.h> | #include <charset.h> | ||||||
| #include <capitalization.h> | #include <capitalization.h> | ||||||
| #include <malloc.h> | #include <malloc.h> | ||||||
|  | @ -18,67 +19,107 @@ static struct capitalization_table capitalization_table[] = | ||||||
| 	CP437_CAPITALIZATION_TABLE; | 	CP437_CAPITALIZATION_TABLE; | ||||||
| #endif | #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; | 	s32 ch = 0; | ||||||
| 	unsigned char c; |  | ||||||
| 
 | 
 | ||||||
| 	if (!src || !*src) | 	ch = read_u8(data); | ||||||
| 		return -1; | 	if (!ch) | ||||||
| 	if (!**src) |  | ||||||
| 		return 0; | 		return 0; | ||||||
| 	c = **src; | 	if (ch >= 0xc2 && ch <= 0xf4) { | ||||||
| 	if (c >= 0x80) { | 		int code = 0; | ||||||
| 		++*src; | 
 | ||||||
| 		if (!**src) | 		if (ch >= 0xe0) { | ||||||
| 			return -1; | 			if (ch >= 0xf0) { | ||||||
| 		/*
 |  | ||||||
| 		 * 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) { |  | ||||||
| 				/* 0xf0 - 0xf4 */ | 				/* 0xf0 - 0xf4 */ | ||||||
| 				c &= 0x07; | 				ch &= 0x07; | ||||||
| 				code = c << 18; | 				code = ch << 18; | ||||||
| 				c = **src; | 				ch = read_u8(data); | ||||||
| 				++*src; | 				if (ch < 0x80 || ch > 0xbf) | ||||||
| 				if (!**src) | 					goto error; | ||||||
| 					return -1; | 				ch &= 0x3f; | ||||||
| 				if (c < 0x80 || c > 0xbf) |  | ||||||
| 					return -1; |  | ||||||
| 				c &= 0x3f; |  | ||||||
| 			} else { | 			} else { | ||||||
| 				/* 0xe0 - 0xef */ | 				/* 0xe0 - 0xef */ | ||||||
| 				c &= 0x0f; | 				ch &= 0x0f; | ||||||
| 			} | 			} | ||||||
| 			code += c << 12; | 			code += ch << 12; | ||||||
| 			if ((code >= 0xD800 && code <= 0xDFFF) || | 			if ((code >= 0xD800 && code <= 0xDFFF) || | ||||||
| 			    code >= 0x110000) | 			    code >= 0x110000) | ||||||
| 				return -1; | 				goto error; | ||||||
| 			c = **src; | 			ch = read_u8(data); | ||||||
| 			++*src; | 			if (ch < 0x80 || ch > 0xbf) | ||||||
| 			if (!**src) | 				goto error; | ||||||
| 				return -1; |  | ||||||
| 			if (c < 0x80 || c > 0xbf) |  | ||||||
| 				return -1; |  | ||||||
| 		} | 		} | ||||||
| 		/* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */ | 		/* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */ | ||||||
| 		c &= 0x3f; | 		ch &= 0x3f; | ||||||
| 		code += c << 6; | 		code += ch << 6; | ||||||
| 		c = **src; | 		ch = read_u8(data); | ||||||
| 		if (c < 0x80 || c > 0xbf) | 		if (ch < 0x80 || ch > 0xbf) | ||||||
| 			return -1; | 			goto error; | ||||||
| 		c &= 0x3f; | 		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; | 	++*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) | int utf8_put(s32 code, char **dst) | ||||||
|  |  | ||||||
|  | @ -449,23 +449,24 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( | ||||||
| 			struct efi_simple_text_input_protocol *this, | 			struct efi_simple_text_input_protocol *this, | ||||||
| 			struct efi_input_key *key) | 			struct efi_input_key *key) | ||||||
| { | { | ||||||
|  | 	efi_status_t ret; | ||||||
| 	struct efi_input_key pressed_key = { | 	struct efi_input_key pressed_key = { | ||||||
| 		.scan_code = 0, | 		.scan_code = 0, | ||||||
| 		.unicode_char = 0, | 		.unicode_char = 0, | ||||||
| 	}; | 	}; | ||||||
| 	char ch; | 	s32 ch; | ||||||
| 
 | 
 | ||||||
| 	EFI_ENTRY("%p, %p", this, key); | 	EFI_ENTRY("%p, %p", this, key); | ||||||
| 
 | 
 | ||||||
| 	/* We don't do interrupts, so check for timers cooperatively */ | 	/* We don't do interrupts, so check for timers cooperatively */ | ||||||
| 	efi_timer_check(); | 	efi_timer_check(); | ||||||
| 
 | 
 | ||||||
| 	if (!tstc()) { | 	ret = console_read_unicode(&ch); | ||||||
| 		/* No key pressed */ | 	if (ret) | ||||||
| 		return EFI_EXIT(EFI_NOT_READY); | 		return EFI_EXIT(EFI_NOT_READY); | ||||||
| 	} | 	/* We do not support multi-word codes */ | ||||||
| 
 | 	if (ch >= 0x10000) | ||||||
| 	ch = getc(); | 		ch = '?'; | ||||||
| 	if (ch == cESC) { | 	if (ch == cESC) { | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * Xterm Control Sequences | 		 * Xterm Control Sequences | ||||||
|  |  | ||||||
|  | @ -178,7 +178,7 @@ static int ut_utf8_utf16_strlen(struct unit_test_state *uts) | ||||||
| 
 | 
 | ||||||
| 	/* illegal utf-8 sequences */ | 	/* illegal utf-8 sequences */ | ||||||
| 	ut_asserteq(4, utf8_utf16_strlen(j1)); | 	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)); | 	ut_asserteq(3, utf8_utf16_strlen(j3)); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -196,7 +196,7 @@ static int ut_utf8_utf16_strnlen(struct unit_test_state *uts) | ||||||
| 
 | 
 | ||||||
| 	/* illegal utf-8 sequences */ | 	/* illegal utf-8 sequences */ | ||||||
| 	ut_asserteq(4, utf8_utf16_strnlen(j1, 16)); | 	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)); | 	ut_asserteq(3, utf8_utf16_strnlen(j3, 16)); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -255,8 +255,8 @@ static int ut_utf8_utf16_strcpy(struct unit_test_state *uts) | ||||||
| 
 | 
 | ||||||
| 	pos = buf; | 	pos = buf; | ||||||
| 	utf8_utf16_strcpy(&pos, j2); | 	utf8_utf16_strcpy(&pos, j2); | ||||||
| 	ut_asserteq(5, pos - buf); | 	ut_asserteq(4, pos - buf); | ||||||
| 	ut_assert(!ut_u16_strcmp(buf, L"j2??l", SIZE_MAX)); | 	ut_assert(!ut_u16_strcmp(buf, L"j2?l", SIZE_MAX)); | ||||||
| 
 | 
 | ||||||
| 	pos = buf; | 	pos = buf; | ||||||
| 	utf8_utf16_strcpy(&pos, j3); | 	utf8_utf16_strcpy(&pos, j3); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue