diff options
author | Jan Engelhardt <jengelh@linux01.gwdg.de> | 2007-07-16 02:40:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:46 -0400 |
commit | 759448f459234bfcf34b82471f0dba77a9aca498 (patch) | |
tree | 61cbf8501bdad78c03e034072791fbf3e0436d43 /drivers/char/selection.c | |
parent | aa0ac36518be648dda3a32f0b37a8b2b546e1b24 (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.c | 48 |
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) */ |
36 | struct vc_data *sel_cons; /* must not be deallocated */ | 37 | struct vc_data *sel_cons; /* must not be deallocated */ |
38 | static int use_unicode; | ||
37 | static volatile int sel_start = -1; /* cleared by clear_selection */ | 39 | static volatile int sel_start = -1; /* cleared by clear_selection */ |
38 | static int sel_end; | 40 | static int sel_end; |
39 | static int sel_buffer_lth; | 41 | static 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 | ||
57 | static unsigned char | 59 | static u16 |
58 | sel_pos(int n) | 60 | sel_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 | ||
89 | static inline int inword(const unsigned char c) { | 92 | static 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) */ | ||
115 | static 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. */ |
112 | int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty) | 136 | int 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, |