Submitted by: Alexander E. Patrakov <patrakov@ums.usu.ru>
Signed-off-by: Alexander E. Patrakov <patrakov@ums.usu.ru>
Date: 2006-07-24
Initial Package Version: 2.6.17.6
Upstream Status: Rejected: they say it modifies the meaning of an existing ioctl
Origin: http://chris.heathens.co.nz/linux/downloads/patches-2.6.4-cdh1.tar.gz
Porting to linux-2.6.16 by Alexander E. Patrakov
Description: 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
diff -Naur linux-2.6.17.6.orig/drivers/char/consolemap.c linux-2.6.17.6/drivers/char/consolemap.c
old
|
new
|
|
178 | 178 | unsigned long refcount; |
179 | 179 | unsigned long sum; |
180 | 180 | unsigned char *inverse_translations[4]; |
| 181 | u16 *inverse_trans_unicode; |
181 | 182 | int readonly; |
182 | 183 | }; |
183 | 184 | |
… |
… |
|
208 | 209 | } |
209 | 210 | } |
210 | 211 | |
| 212 | static void set_inverse_trans_unicode(struct vc_data *conp, |
| 213 | struct uni_pagedir *p) |
| 214 | { |
| 215 | int i, j, k, glyph; |
| 216 | u16 **p1, *p2; |
| 217 | u16 *q; |
| 218 | |
| 219 | if (!p) return; |
| 220 | q = p->inverse_trans_unicode; |
| 221 | if (!q) { |
| 222 | q = p->inverse_trans_unicode = |
| 223 | kmalloc(MAX_GLYPH * sizeof(u16), GFP_KERNEL); |
| 224 | if (!q) |
| 225 | return; |
| 226 | } |
| 227 | memset(q, 0, MAX_GLYPH * sizeof(u16)); |
| 228 | |
| 229 | for (i = 0; i < 32; i++) { |
| 230 | p1 = p->uni_pgdir[i]; |
| 231 | if (!p1) |
| 232 | continue; |
| 233 | for (j = 0; j < 32; j++) { |
| 234 | p2 = p1[j]; |
| 235 | if (!p2) |
| 236 | continue; |
| 237 | for (k = 0; k < 64; k++) { |
| 238 | glyph = p2[k]; |
| 239 | if (glyph >= 0 && glyph < MAX_GLYPH |
| 240 | && q[glyph] < 32) |
| 241 | q[glyph] = (i << 11) + (j << 6) + k; |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | |
211 | 247 | unsigned short *set_translate(int m, struct vc_data *vc) |
212 | 248 | { |
213 | 249 | inv_translate[vc->vc_num] = m; |
… |
… |
|
218 | 254 | * Inverse translation is impossible for several reasons: |
219 | 255 | * 1. The font<->character maps are not 1-1. |
220 | 256 | * 2. The text may have been written while a different translation map |
221 | | * was active, or using Unicode. |
| 257 | * was active. |
222 | 258 | * Still, it is now possible to a certain extent to cut and paste non-ASCII. |
223 | 259 | */ |
224 | | unsigned char inverse_translate(struct vc_data *conp, int glyph) |
| 260 | u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) |
225 | 261 | { |
226 | 262 | struct uni_pagedir *p; |
| 263 | int m; |
227 | 264 | if (glyph < 0 || glyph >= MAX_GLYPH) |
228 | 265 | return 0; |
229 | | else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc) || |
230 | | !p->inverse_translations[inv_translate[conp->vc_num]]) |
| 266 | else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc)) |
231 | 267 | return glyph; |
232 | | else |
233 | | return p->inverse_translations[inv_translate[conp->vc_num]][glyph]; |
| 268 | else if (use_unicode) { |
| 269 | if (!p->inverse_trans_unicode) |
| 270 | return glyph; |
| 271 | else |
| 272 | return p->inverse_trans_unicode[glyph]; |
| 273 | } else { |
| 274 | m = inv_translate[conp->vc_num]; |
| 275 | if (!p->inverse_translations[m]) |
| 276 | return glyph; |
| 277 | else |
| 278 | return p->inverse_translations[m][glyph]; |
| 279 | } |
234 | 280 | } |
235 | 281 | |
236 | 282 | static void update_user_maps(void) |
… |
… |
|
244 | 290 | p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; |
245 | 291 | if (p && p != q) { |
246 | 292 | set_inverse_transl(vc_cons[i].d, p, USER_MAP); |
| 293 | set_inverse_trans_unicode(vc_cons[i].d, p); |
247 | 294 | q = p; |
248 | 295 | } |
249 | 296 | } |
… |
… |
|
354 | 401 | kfree(p->inverse_translations[i]); |
355 | 402 | p->inverse_translations[i] = NULL; |
356 | 403 | } |
| 404 | if (p->inverse_trans_unicode) { |
| 405 | kfree(p->inverse_trans_unicode); |
| 406 | p->inverse_trans_unicode = NULL; |
| 407 | } |
357 | 408 | } |
358 | 409 | |
359 | 410 | void con_free_unimap(struct vc_data *vc) |
… |
… |
|
512 | 563 | |
513 | 564 | for (i = 0; i <= 3; i++) |
514 | 565 | set_inverse_transl(vc, p, i); /* Update all inverse translations */ |
| 566 | set_inverse_trans_unicode(vc, p); |
515 | 567 | |
516 | 568 | return err; |
517 | 569 | } |
… |
… |
|
562 | 614 | |
563 | 615 | for (i = 0; i <= 3; i++) |
564 | 616 | set_inverse_transl(vc, p, i); /* Update all inverse translations */ |
| 617 | set_inverse_trans_unicode(vc, p); |
565 | 618 | dflt = p; |
566 | 619 | return err; |
567 | 620 | } |
… |
… |
|
618 | 671 | p->readonly = rdonly; |
619 | 672 | } |
620 | 673 | |
| 674 | /* may be called during an interrupt */ |
| 675 | u32 conv_8bit_to_uni(unsigned char c) |
| 676 | { |
| 677 | /* |
| 678 | * Always use USER_MAP. This function is used by the keyboard, |
| 679 | * which shouldn't be affected by G0/G1 switching, etc. |
| 680 | * If the user map still contains default values, i.e. the |
| 681 | * direct-to-font mapping, then assume user is using Latin1. |
| 682 | */ |
| 683 | unsigned short uni = translations[USER_MAP][c]; |
| 684 | return uni == (0xf000 | c) ? c : uni; |
| 685 | } |
| 686 | |
621 | 687 | int |
622 | 688 | conv_uni_to_pc(struct vc_data *conp, long ucs) |
623 | 689 | { |
diff -Naur linux-2.6.17.6.orig/drivers/char/keyboard.c linux-2.6.17.6/drivers/char/keyboard.c
old
|
new
|
|
34 | 34 | #include <linux/init.h> |
35 | 35 | #include <linux/slab.h> |
36 | 36 | |
| 37 | #include <linux/consolemap.h> |
37 | 38 | #include <linux/kbd_kern.h> |
38 | 39 | #include <linux/kbd_diacr.h> |
39 | 40 | #include <linux/vt_kern.h> |
… |
… |
|
329 | 330 | * Many other routines do put_queue, but I think either |
330 | 331 | * they produce ASCII, or they produce some user-assigned |
331 | 332 | * string, and in both cases we might assume that it is |
332 | | * in utf-8 already. UTF-8 is defined for words of up to 31 bits, |
333 | | * but we need only 16 bits here |
| 333 | * in utf-8 already. |
334 | 334 | */ |
335 | | static void to_utf8(struct vc_data *vc, ushort c) |
| 335 | static void to_utf8(struct vc_data *vc, uint c) |
336 | 336 | { |
337 | 337 | if (c < 0x80) |
338 | 338 | /* 0******* */ |
… |
… |
|
341 | 341 | /* 110***** 10****** */ |
342 | 342 | put_queue(vc, 0xc0 | (c >> 6)); |
343 | 343 | put_queue(vc, 0x80 | (c & 0x3f)); |
344 | | } else { |
| 344 | } else if (c < 0x10000) { |
| 345 | if (c >= 0xD800 && c < 0xE000) |
| 346 | return; |
| 347 | if (c == 0xFFFF) |
| 348 | return; |
345 | 349 | /* 1110**** 10****** 10****** */ |
346 | 350 | put_queue(vc, 0xe0 | (c >> 12)); |
347 | 351 | put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); |
348 | 352 | put_queue(vc, 0x80 | (c & 0x3f)); |
| 353 | } else if (c < 0x110000) { |
| 354 | /* 11110*** 10****** 10****** 10****** */ |
| 355 | put_queue(vc, 0xf0 | (c >> 18)); |
| 356 | put_queue(vc, 0x80 | ((c >> 12) & 0x3f)); |
| 357 | put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); |
| 358 | put_queue(vc, 0x80 | (c & 0x3f)); |
349 | 359 | } |
350 | 360 | } |
351 | 361 | |
| 362 | static void put_8bit(struct vc_data *vc, u8 c) |
| 363 | { |
| 364 | if (kbd->kbdmode != VC_UNICODE || c < 32 || c == 127) |
| 365 | /* Don't translate control chars */ |
| 366 | put_queue(vc, c); |
| 367 | else |
| 368 | to_utf8(vc, conv_8bit_to_uni(c)); |
| 369 | } |
| 370 | |
352 | 371 | /* |
353 | 372 | * Called after returning from RAW mode or when changing consoles - recompute |
354 | 373 | * shift_down[] and shift_state from key_down[] maybe called when keymap is |
… |
… |
|
416 | 435 | if (kbd->kbdmode == VC_UNICODE) |
417 | 436 | to_utf8(vc, d); |
418 | 437 | else if (d < 0x100) |
419 | | put_queue(vc, d); |
| 438 | put_8bit(vc, d); |
420 | 439 | |
421 | 440 | return ch; |
422 | 441 | } |
… |
… |
|
430 | 449 | if (kbd->kbdmode == VC_UNICODE) |
431 | 450 | to_utf8(vc, diacr); |
432 | 451 | else if (diacr < 0x100) |
433 | | put_queue(vc, diacr); |
| 452 | put_8bit(vc, diacr); |
434 | 453 | diacr = 0; |
435 | 454 | } |
436 | 455 | put_queue(vc, 13); |
… |
… |
|
798 | 817 | /* kludge */ |
799 | 818 | if (up_flag && shift_state != old_state && npadch != -1) { |
800 | 819 | if (kbd->kbdmode == VC_UNICODE) |
801 | | to_utf8(vc, npadch & 0xffff); |
| 820 | to_utf8(vc, npadch); |
802 | 821 | else |
803 | 822 | put_queue(vc, npadch & 0xff); |
804 | 823 | npadch = -1; |
diff -Naur linux-2.6.17.6.orig/drivers/char/selection.c linux-2.6.17.6/drivers/char/selection.c
old
|
new
|
|
20 | 20 | |
21 | 21 | #include <asm/uaccess.h> |
22 | 22 | |
| 23 | #include <linux/kbd_kern.h> |
23 | 24 | #include <linux/vt_kern.h> |
24 | 25 | #include <linux/consolemap.h> |
25 | 26 | #include <linux/selection.h> |
… |
… |
|
34 | 35 | /* Variables for selection control. */ |
35 | 36 | /* Use a dynamic buffer, instead of static (Dec 1994) */ |
36 | 37 | struct vc_data *sel_cons; /* must not be disallocated */ |
| 38 | static int use_unicode; |
37 | 39 | static volatile int sel_start = -1; /* cleared by clear_selection */ |
38 | 40 | static int sel_end; |
39 | 41 | static int sel_buffer_lth; |
… |
… |
|
54 | 56 | complement_pos(sel_cons, where); |
55 | 57 | } |
56 | 58 | |
57 | | static unsigned char |
| 59 | static u16 |
58 | 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 | 66 | /* remove the current selection highlight, if any, |
… |
… |
|
86 | 89 | 0xFF7FFFFF /* latin-1 accented letters, not division sign */ |
87 | 90 | }; |
88 | 91 | |
89 | | static inline int inword(const unsigned char c) { |
90 | | return ( inwordLut[c>>5] >> (c & 0x1F) ) & 1; |
| 92 | static inline int inword(const u16 c) { |
| 93 | return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1); |
91 | 94 | } |
92 | 95 | |
93 | 96 | /* set inwordLut contents. Invoked by ioctl(). */ |
… |
… |
|
108 | 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 | 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 | 135 | /* set the current selection. Invoked by ioctl() or by kernel code. */ |
112 | 136 | int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty) |
113 | 137 | { |
114 | 138 | struct vc_data *vc = vc_cons[fg_console].d; |
115 | 139 | int sel_mode, new_sel_start, new_sel_end, spc; |
116 | 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 | 145 | poke_blanked_console(); |
120 | 146 | |
… |
… |
|
158 | 184 | clear_selection(); |
159 | 185 | sel_cons = vc_cons[fg_console].d; |
160 | 186 | } |
161 | | |
| 187 | use_unicode = kbd && kbd->kbdmode == VC_UNICODE; |
| 188 | |
162 | 189 | switch (sel_mode) |
163 | 190 | { |
164 | 191 | case TIOCL_SELCHAR: /* character-by-character selection */ |
… |
… |
|
240 | 267 | sel_end = new_sel_end; |
241 | 268 | |
242 | 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 | 272 | if (!bp) { |
245 | 273 | printk(KERN_WARNING "selection: kmalloc() failed\n"); |
246 | 274 | clear_selection(); |
… |
… |
|
251 | 279 | |
252 | 280 | obp = bp; |
253 | 281 | for (i = sel_start; i <= sel_end; i += 2) { |
254 | | *bp = sel_pos(i); |
255 | | if (!isspace(*bp++)) |
| 282 | c = sel_pos(i); |
| 283 | if (use_unicode) |
| 284 | bp += store_utf8(c, bp); |
| 285 | else |
| 286 | *bp++ = c; |
| 287 | if (!isspace(c)) |
256 | 288 | obp = bp; |
257 | 289 | if (! ((i + 2) % vc->vc_size_row)) { |
258 | 290 | /* strip trailing blanks from line and add newline, |
diff -Naur linux-2.6.17.6.orig/include/linux/consolemap.h linux-2.6.17.6/include/linux/consolemap.h
old
|
new
|
|
10 | 10 | |
11 | 11 | struct vc_data; |
12 | 12 | |
13 | | extern unsigned char inverse_translate(struct vc_data *conp, int glyph); |
| 13 | extern u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode); |
14 | 14 | extern unsigned short *set_translate(int m, struct vc_data *vc); |
15 | 15 | extern int conv_uni_to_pc(struct vc_data *conp, long ucs); |
| 16 | extern u32 conv_8bit_to_uni(unsigned char c); |