aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/selection.c
diff options
context:
space:
mode:
authorJan Engelhardt <jengelh@linux01.gwdg.de>2007-07-16 02:40:40 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-16 12:05:46 -0400
commit759448f459234bfcf34b82471f0dba77a9aca498 (patch)
tree61cbf8501bdad78c03e034072791fbf3e0436d43 /drivers/char/selection.c
parentaa0ac36518be648dda3a32f0b37a8b2b546e1b24 (diff)
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>
Diffstat (limited to 'drivers/char/selection.c')
-rw-r--r--drivers/char/selection.c48
1 files changed, 40 insertions, 8 deletions
diff --git a/drivers/char/selection.c b/drivers/char/selection.c
index a69f094d1ed3..d63f5ccc29e6 100644
--- a/drivers/char/selection.c
+++ b/drivers/char/selection.c
@@ -20,6 +20,7 @@
20 20
21#include <asm/uaccess.h> 21#include <asm/uaccess.h>
22 22
23#include <linux/kbd_kern.h>
23#include <linux/vt_kern.h> 24#include <linux/vt_kern.h>
24#include <linux/consolemap.h> 25#include <linux/consolemap.h>
25#include <linux/selection.h> 26#include <linux/selection.h>
@@ -34,6 +35,7 @@ extern void poke_blanked_console(void);
34/* Variables for selection control. */ 35/* Variables for selection control. */
35/* Use a dynamic buffer, instead of static (Dec 1994) */ 36/* Use a dynamic buffer, instead of static (Dec 1994) */
36struct vc_data *sel_cons; /* must not be deallocated */ 37struct vc_data *sel_cons; /* must not be deallocated */
38static int use_unicode;
37static volatile int sel_start = -1; /* cleared by clear_selection */ 39static volatile int sel_start = -1; /* cleared by clear_selection */
38static int sel_end; 40static int sel_end;
39static int sel_buffer_lth; 41static int sel_buffer_lth;
@@ -54,10 +56,11 @@ static inline void highlight_pointer(const int where)
54 complement_pos(sel_cons, where); 56 complement_pos(sel_cons, where);
55} 57}
56 58
57static unsigned char 59static u16
58sel_pos(int n) 60sel_pos(int n)
59{ 61{
60 return inverse_translate(sel_cons, screen_glyph(sel_cons, n)); 62 return inverse_translate(sel_cons, screen_glyph(sel_cons, n),
63 use_unicode);
61} 64}
62 65
63/* remove the current selection highlight, if any, 66/* remove the current selection highlight, if any,
@@ -86,8 +89,8 @@ static u32 inwordLut[8]={
86 0xFF7FFFFF /* latin-1 accented letters, not division sign */ 89 0xFF7FFFFF /* latin-1 accented letters, not division sign */
87}; 90};
88 91
89static inline int inword(const unsigned char c) { 92static inline int inword(const u16 c) {
90 return ( inwordLut[c>>5] >> (c & 0x1F) ) & 1; 93 return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
91} 94}
92 95
93/* set inwordLut contents. Invoked by ioctl(). */ 96/* set inwordLut contents. Invoked by ioctl(). */
@@ -108,13 +111,36 @@ static inline unsigned short limit(const unsigned short v, const unsigned short
108 return (v > u) ? u : v; 111 return (v > u) ? u : v;
109} 112}
110 113
114/* stores the char in UTF8 and returns the number of bytes used (1-3) */
115static int store_utf8(u16 c, char *p)
116{
117 if (c < 0x80) {
118 /* 0******* */
119 p[0] = c;
120 return 1;
121 } else if (c < 0x800) {
122 /* 110***** 10****** */
123 p[0] = 0xc0 | (c >> 6);
124 p[1] = 0x80 | (c & 0x3f);
125 return 2;
126 } else {
127 /* 1110**** 10****** 10****** */
128 p[0] = 0xe0 | (c >> 12);
129 p[1] = 0x80 | ((c >> 6) & 0x3f);
130 p[2] = 0x80 | (c & 0x3f);
131 return 3;
132 }
133}
134
111/* set the current selection. Invoked by ioctl() or by kernel code. */ 135/* set the current selection. Invoked by ioctl() or by kernel code. */
112int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty) 136int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty)
113{ 137{
114 struct vc_data *vc = vc_cons[fg_console].d; 138 struct vc_data *vc = vc_cons[fg_console].d;
115 int sel_mode, new_sel_start, new_sel_end, spc; 139 int sel_mode, new_sel_start, new_sel_end, spc;
116 char *bp, *obp; 140 char *bp, *obp;
117 int i, ps, pe; 141 int i, ps, pe, multiplier;
142 u16 c;
143 struct kbd_struct *kbd = kbd_table + fg_console;
118 144
119 poke_blanked_console(); 145 poke_blanked_console();
120 146
@@ -158,6 +184,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
158 clear_selection(); 184 clear_selection();
159 sel_cons = vc_cons[fg_console].d; 185 sel_cons = vc_cons[fg_console].d;
160 } 186 }
187 use_unicode = kbd && kbd->kbdmode == VC_UNICODE;
161 188
162 switch (sel_mode) 189 switch (sel_mode)
163 { 190 {
@@ -240,7 +267,8 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
240 sel_end = new_sel_end; 267 sel_end = new_sel_end;
241 268
242 /* Allocate a new buffer before freeing the old one ... */ 269 /* Allocate a new buffer before freeing the old one ... */
243 bp = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL); 270 multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */
271 bp = kmalloc((sel_end-sel_start)/2*multiplier+1, GFP_KERNEL);
244 if (!bp) { 272 if (!bp) {
245 printk(KERN_WARNING "selection: kmalloc() failed\n"); 273 printk(KERN_WARNING "selection: kmalloc() failed\n");
246 clear_selection(); 274 clear_selection();
@@ -251,8 +279,12 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
251 279
252 obp = bp; 280 obp = bp;
253 for (i = sel_start; i <= sel_end; i += 2) { 281 for (i = sel_start; i <= sel_end; i += 2) {
254 *bp = sel_pos(i); 282 c = sel_pos(i);
255 if (!isspace(*bp++)) 283 if (use_unicode)
284 bp += store_utf8(c, bp);
285 else
286 *bp++ = c;
287 if (!isspace(c))
256 obp = bp; 288 obp = bp;
257 if (! ((i + 2) % vc->vc_size_row)) { 289 if (! ((i + 2) % vc->vc_size_row)) {
258 /* strip trailing blanks from line and add newline, 290 /* strip trailing blanks from line and add newline,