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:
committed by
Linus Torvalds
parent
aa0ac36518
commit
759448f459
@@ -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,
|
||||
|
Reference in New Issue
Block a user