Kernel utf-8 handling

This patch fixes dead keys and copy/paste of non-ASCII characters in UTF-8
mode on Linux console.  See more details about the original patch at:
http://chris.heathens.co.nz/linux/utf8.html

Already posted on
	(Oldest) http://lkml.org/lkml/2003/5/31/148
	         http://lkml.org/lkml/2005/12/24/69
	(Recent) http://lkml.org/lkml/2006/8/7/75

[bunk@stusta.de: make drivers/char/selection.c:store_utf8() static]
Signed-off-by: Jan Engelhardt <jengelh@gmx.de>
Cc: Alexander E. Patrakov <patrakov@ums.usu.ru>
Cc: Dmitry Torokhov <dtor@mail.ru>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Cc: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Jan Engelhardt
2007-07-15 23:40:40 -07:00
committed by Linus Torvalds
parent aa0ac36518
commit 759448f459
4 changed files with 134 additions and 23 deletions

View File

@@ -20,6 +20,7 @@
#include <asm/uaccess.h>
#include <linux/kbd_kern.h>
#include <linux/vt_kern.h>
#include <linux/consolemap.h>
#include <linux/selection.h>
@@ -34,6 +35,7 @@ extern void poke_blanked_console(void);
/* Variables for selection control. */
/* Use a dynamic buffer, instead of static (Dec 1994) */
struct vc_data *sel_cons; /* must not be deallocated */
static int use_unicode;
static volatile int sel_start = -1; /* cleared by clear_selection */
static int sel_end;
static int sel_buffer_lth;
@@ -54,10 +56,11 @@ static inline void highlight_pointer(const int where)
complement_pos(sel_cons, where);
}
static unsigned char
static u16
sel_pos(int n)
{
return inverse_translate(sel_cons, screen_glyph(sel_cons, n));
return inverse_translate(sel_cons, screen_glyph(sel_cons, n),
use_unicode);
}
/* remove the current selection highlight, if any,
@@ -86,8 +89,8 @@ static u32 inwordLut[8]={
0xFF7FFFFF /* latin-1 accented letters, not division sign */
};
static inline int inword(const unsigned char c) {
return ( inwordLut[c>>5] >> (c & 0x1F) ) & 1;
static inline int inword(const u16 c) {
return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
}
/* set inwordLut contents. Invoked by ioctl(). */
@@ -108,13 +111,36 @@ static inline unsigned short limit(const unsigned short v, const unsigned short
return (v > u) ? u : v;
}
/* stores the char in UTF8 and returns the number of bytes used (1-3) */
static int store_utf8(u16 c, char *p)
{
if (c < 0x80) {
/* 0******* */
p[0] = c;
return 1;
} else if (c < 0x800) {
/* 110***** 10****** */
p[0] = 0xc0 | (c >> 6);
p[1] = 0x80 | (c & 0x3f);
return 2;
} else {
/* 1110**** 10****** 10****** */
p[0] = 0xe0 | (c >> 12);
p[1] = 0x80 | ((c >> 6) & 0x3f);
p[2] = 0x80 | (c & 0x3f);
return 3;
}
}
/* set the current selection. Invoked by ioctl() or by kernel code. */
int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty)
{
struct vc_data *vc = vc_cons[fg_console].d;
int sel_mode, new_sel_start, new_sel_end, spc;
char *bp, *obp;
int i, ps, pe;
int i, ps, pe, multiplier;
u16 c;
struct kbd_struct *kbd = kbd_table + fg_console;
poke_blanked_console();
@@ -158,6 +184,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
clear_selection();
sel_cons = vc_cons[fg_console].d;
}
use_unicode = kbd && kbd->kbdmode == VC_UNICODE;
switch (sel_mode)
{
@@ -240,7 +267,8 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
sel_end = new_sel_end;
/* Allocate a new buffer before freeing the old one ... */
bp = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL);
multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */
bp = kmalloc((sel_end-sel_start)/2*multiplier+1, GFP_KERNEL);
if (!bp) {
printk(KERN_WARNING "selection: kmalloc() failed\n");
clear_selection();
@@ -251,8 +279,12 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
obp = bp;
for (i = sel_start; i <= sel_end; i += 2) {
*bp = sel_pos(i);
if (!isspace(*bp++))
c = sel_pos(i);
if (use_unicode)
bp += store_utf8(c, bp);
else
*bp++ = c;
if (!isspace(c))
obp = bp;
if (! ((i + 2) % vc->vc_size_row)) {
/* strip trailing blanks from line and add newline,