diff mbox series

[6/8] vt: add ucs_get_fallback()

Message ID 20250505170021.29944-7-nico@fluxnic.net
State New
Headers show
Series vt: more Unicode handling changes | expand

Commit Message

Nicolas Pitre May 5, 2025, 4:55 p.m. UTC
From: Nicolas Pitre <npitre@baylibre.com>

This is the code querying the newly introduced tables.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
---
 drivers/tty/vt/Makefile    |  3 +-
 drivers/tty/vt/ucs.c       | 73 ++++++++++++++++++++++++++++++++++++--
 include/linux/consolemap.h |  6 ++++
 3 files changed, 78 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile
index 509362a3e11e..ae746dcdeec8 100644
--- a/drivers/tty/vt/Makefile
+++ b/drivers/tty/vt/Makefile
@@ -36,7 +36,8 @@  $(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map
 
 endif
 
-$(obj)/ucs.o: $(src)/ucs.c $(obj)/ucs_width_table.h $(obj)/ucs_recompose_table.h
+$(obj)/ucs.o:	$(src)/ucs.c $(obj)/ucs_width_table.h \
+		$(obj)/ucs_recompose_table.h $(obj)/ucs_fallback_table.h
 
 # You may uncomment one of those to have the UCS tables be regenerated
 # during the build process. By default the _shipped versions are used.
diff --git a/drivers/tty/vt/ucs.c b/drivers/tty/vt/ucs.c
index b0b23830170d..dcce733b80cb 100644
--- a/drivers/tty/vt/ucs.c
+++ b/drivers/tty/vt/ucs.c
@@ -44,13 +44,20 @@  static int interval32_cmp(const void *key, const void *element)
 	return 0;
 }
 
-static bool cp_in_range16(u16 cp, const struct ucs_interval16 *ranges, size_t size)
+static const struct ucs_interval16 *find_cp_in_range16(u16 cp,
+						       const struct ucs_interval16 *ranges,
+						       size_t size)
 {
 	if (cp < ranges[0].first || cp > ranges[size - 1].last)
-		return false;
+		return NULL;
 
 	return __inline_bsearch(&cp, ranges, size, sizeof(*ranges),
-				interval16_cmp) != NULL;
+				interval16_cmp);
+}
+
+static bool cp_in_range16(u16 cp, const struct ucs_interval16 *ranges, size_t size)
+{
+	return find_cp_in_range16(cp, ranges, size) != NULL;
 }
 
 static bool cp_in_range32(u32 cp, const struct ucs_interval32 *ranges, size_t size)
@@ -157,3 +164,63 @@  u32 ucs_recompose(u32 base, u32 mark)
 
 	return result ? result->recomposed : 0;
 }
+
+/*
+ * The fallback tables are using struct ucs_interval16 or plain literals
+ * directly. We reuse interval16_cmp() for the former, but another compare
+ * function is needed in the singles case.
+ */
+
+#include "ucs_fallback_table.h"
+
+static int u16_cmp(const void *key, const void *element)
+{
+	u16 cp = *(u16 *)key;
+	u16 entry = *(u16 *)element;
+
+	if (cp < entry)
+		return -1;
+	if (cp > entry)
+		return 1;
+	return 0;
+}
+
+static u16 *find_cp_in_table16(u16 cp, const u16 *table, size_t size)
+{
+	if (cp < table[0] || cp > table[size - 1])
+		return NULL;
+
+	return __inline_bsearch(&cp, table, size, sizeof(u16), u16_cmp);
+}
+
+/**
+ * ucs_get_fallback() - Get a substitution for the provided Unicode character
+ * @base: Base Unicode code point (UCS-4)
+ *
+ * Get a simpler fallback character for the provided Unicode character.
+ * This is used for terminal display when corresponding glyph is unavailable.
+ * The substitution may not be as good as the actual glyph for the original
+ * character but still way more helpful than a squared question mark.
+ *
+ * Return: Fallback Unicode code point, or 0 if none is available
+ */
+u32 ucs_get_fallback(u32 cp)
+{
+	const struct ucs_interval16 *interval;
+	u16 *single;
+
+	if (!UCS_IS_BMP(cp))
+		return 0;
+
+	interval = find_cp_in_range16(cp, ucs_fallback_intervals,
+				      ARRAY_SIZE(ucs_fallback_intervals));
+	if (interval)
+		return ucs_fallback_intervals_subs[interval - ucs_fallback_intervals];
+
+	single = find_cp_in_table16(cp, ucs_fallback_singles,
+				    ARRAY_SIZE(ucs_fallback_singles));
+	if (single)
+		return ucs_fallback_singles_subs[single - ucs_fallback_singles];
+
+	return 0;
+}
diff --git a/include/linux/consolemap.h b/include/linux/consolemap.h
index 8167494229db..6180b803795c 100644
--- a/include/linux/consolemap.h
+++ b/include/linux/consolemap.h
@@ -31,6 +31,7 @@  void console_map_init(void);
 bool ucs_is_double_width(uint32_t cp);
 bool ucs_is_zero_width(uint32_t cp);
 u32 ucs_recompose(u32 base, u32 mark);
+u32 ucs_get_fallback(u32 cp);
 #else
 static inline u16 inverse_translate(const struct vc_data *conp, u16 glyph,
 		bool use_unicode)
@@ -75,6 +76,11 @@  static inline u32 ucs_recompose(u32 base, u32 mark)
 {
 	return 0;
 }
+
+static inline u32 ucs_get_fallback(u32 cp)
+{
+	return 0;
+}
 #endif /* CONFIG_CONSOLE_TRANSLATIONS */
 
 #endif /* __LINUX_CONSOLEMAP_H__ */