unicode diacritics support
authorSamuel Thibault <samuel.thibault@ens-lyon.org>
Wed, 17 Oct 2007 06:27:04 +0000 (23:27 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Wed, 17 Oct 2007 15:42:52 +0000 (08:42 -0700)
There have been issues with non-latin1 diacritics and unicode.
http://bugzilla.kernel.org/show_bug.cgi?id=7746

Git 759448f459234bfcf34b82471f0dba77a9aca498 `Kernel utf-8 handling'
partly resolved it by adding conversion between diacritics and
unicode. The patch below goes further by just turning diacritics into
unicode, hence providing better future support. The kbd support can be
fetched from
http://bugzilla.kernel.org/attachment.cgi?id=12313

This was tested in all of latin1, latin9, latin2 and unicode with french
and czech dead keys.

Turn the kernel accent_table into unicode, and extend ioctls KDGKBDIACR
and KDSKBDIACR into their equivalents KDGKBDIACRUC and KDSKBDIACR.

New function int conv_uni_to_8bit(u32 uni) for converting unicode into 8bit
_input_.  No, we don't want to store the translation, as it is potentially
sparse and large.

Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Cc: Jan Engelhardt <jengelh@gmx.de>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
12 files changed:
drivers/acorn/char/defkeymap-l7200.c
drivers/char/consolemap.c
drivers/char/defkeymap.c_shipped
drivers/char/keyboard.c
drivers/char/vt_ioctl.c
drivers/s390/char/defkeymap.c
drivers/s390/char/keyboard.c
drivers/s390/char/keyboard.h
drivers/tc/lk201-map.c_shipped
include/linux/consolemap.h
include/linux/kbd_diacr.h
include/linux/kd.h

index 9e18ce742e3892f9377811218609408988cef64c..28a5fbc6aa1a456d335852914aa24ea48880c74b 100644 (file)
@@ -346,7 +346,7 @@ char *func_table[MAX_NR_FUNC] = {
        0,
 };
 
-struct kbdiacr accent_table[MAX_DIACR] = {
+struct kbdiacruc accent_table[MAX_DIACR] = {
        {'`', 'A', '\300'},     {'`', 'a', '\340'},
        {'\'', 'A', '\301'},    {'\'', 'a', '\341'},
        {'^', 'A', '\302'},     {'^', 'a', '\342'},
index 163f3fce3f84e2ca2ea827f5bdd6acd78506194e..6b104e45a322b3a5bd321a07f12a4c3936441db6 100644 (file)
@@ -669,19 +669,29 @@ void con_protect_unimap(struct vc_data *vc, int rdonly)
                p->readonly = rdonly;
 }
 
+/*
+ * Always use USER_MAP. These functions are used by the keyboard,
+ * which shouldn't be affected by G0/G1 switching, etc.
+ * If the user map still contains default values, i.e. the
+ * direct-to-font mapping, then assume user is using Latin1.
+ */
 /* may be called during an interrupt */
 u32 conv_8bit_to_uni(unsigned char c)
 {
-       /*
-        * Always use USER_MAP. This function is used by the keyboard,
-        * which shouldn't be affected by G0/G1 switching, etc.
-        * If the user map still contains default values, i.e. the
-        * direct-to-font mapping, then assume user is using Latin1.
-        */
        unsigned short uni = translations[USER_MAP][c];
        return uni == (0xf000 | c) ? c : uni;
 }
 
+int conv_uni_to_8bit(u32 uni)
+{
+       int c;
+       for (c = 0; c < 0x100; c++)
+               if (translations[USER_MAP][c] == uni ||
+                  (translations[USER_MAP][c] == (c | 0xf000) && uni == c))
+                       return c;
+       return -1;
+}
+
 int
 conv_uni_to_pc(struct vc_data *conp, long ucs) 
 {
index 453a2f1ffa157b572d47ce4606da2282f267340e..0aa419a617674dfe20bb6d8dfe09aa706ad5cf18 100644 (file)
@@ -222,7 +222,7 @@ char *func_table[MAX_NR_FUNC] = {
        NULL,
 };
 
-struct kbdiacr accent_table[MAX_DIACR] = {
+struct kbdiacruc accent_table[MAX_DIACR] = {
        {'`', 'A', '\300'},     {'`', 'a', '\340'},
        {'\'', 'A', '\301'},    {'\'', 'a', '\341'},
        {'^', 'A', '\302'},     {'^', 'a', '\342'},
index d95f316afb5a9d1c3328046c8d9f4f8ab7743981..5ae2a3250c50dcdf1aa987800dd30a73dc1d275f 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/kbd_kern.h>
 #include <linux/kbd_diacr.h>
 #include <linux/vt_kern.h>
+#include <linux/consolemap.h>
 #include <linux/sysrq.h>
 #include <linux/input.h>
 #include <linux/reboot.h>
@@ -403,9 +404,12 @@ static unsigned int handle_diacr(struct vc_data *vc, unsigned int ch)
                return d;
 
        if (kbd->kbdmode == VC_UNICODE)
-               to_utf8(vc, conv_8bit_to_uni(d));
-       else if (d < 0x100)
-               put_queue(vc, d);
+               to_utf8(vc, d);
+       else {
+               int c = conv_uni_to_8bit(d);
+               if (c != -1)
+                       put_queue(vc, c);
+       }
 
        return ch;
 }
@@ -417,9 +421,12 @@ static void fn_enter(struct vc_data *vc)
 {
        if (diacr) {
                if (kbd->kbdmode == VC_UNICODE)
-                       to_utf8(vc, conv_8bit_to_uni(diacr));
-               else if (diacr < 0x100)
-                       put_queue(vc, diacr);
+                       to_utf8(vc, diacr);
+               else {
+                       int c = conv_uni_to_8bit(diacr);
+                       if (c != -1)
+                               put_queue(vc, c);
+               }
                diacr = 0;
        }
        put_queue(vc, 13);
@@ -627,9 +634,12 @@ static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag)
                return;
        }
        if (kbd->kbdmode == VC_UNICODE)
-               to_utf8(vc, conv_8bit_to_uni(value));
-       else if (value < 0x100)
-               put_queue(vc, value);
+               to_utf8(vc, value);
+       else {
+               int c = conv_uni_to_8bit(value);
+               if (c != -1)
+                       put_queue(vc, c);
+       }
 }
 
 /*
@@ -646,7 +656,12 @@ static void k_deadunicode(struct vc_data *vc, unsigned int value, char up_flag)
 
 static void k_self(struct vc_data *vc, unsigned char value, char up_flag)
 {
-       k_unicode(vc, value, up_flag);
+       unsigned int uni;
+       if (kbd->kbdmode == VC_UNICODE)
+               uni = value;
+       else
+               uni = conv_8bit_to_uni(value);
+       k_unicode(vc, uni, up_flag);
 }
 
 static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag)
index f69a8258095cf6de5129f0ba9190f060ae5e5628..6c7384afff13ceeba11c6f4d8cdbfc15e959b0a4 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/major.h>
 #include <linux/fs.h>
 #include <linux/console.h>
+#include <linux/consolemap.h>
 #include <linux/signal.h>
 #include <linux/timex.h>
 
@@ -582,10 +583,27 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
        case KDGKBDIACR:
        {
                struct kbdiacrs __user *a = up;
+               struct kbdiacr diacr;
+               int i;
 
                if (put_user(accent_table_size, &a->kb_cnt))
                        return -EFAULT;
-               if (copy_to_user(a->kbdiacr, accent_table, accent_table_size*sizeof(struct kbdiacr)))
+               for (i = 0; i < accent_table_size; i++) {
+                       diacr.diacr = conv_uni_to_8bit(accent_table[i].diacr);
+                       diacr.base = conv_uni_to_8bit(accent_table[i].base);
+                       diacr.result = conv_uni_to_8bit(accent_table[i].result);
+                       if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr)))
+                               return -EFAULT;
+               }
+               return 0;
+       }
+       case KDGKBDIACRUC:
+       {
+               struct kbdiacrsuc __user *a = up;
+
+               if (put_user(accent_table_size, &a->kb_cnt))
+                       return -EFAULT;
+               if (copy_to_user(a->kbdiacruc, accent_table, accent_table_size*sizeof(struct kbdiacruc)))
                        return -EFAULT;
                return 0;
        }
@@ -593,6 +611,30 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
        case KDSKBDIACR:
        {
                struct kbdiacrs __user *a = up;
+               struct kbdiacr diacr;
+               unsigned int ct;
+               int i;
+
+               if (!perm)
+                       return -EPERM;
+               if (get_user(ct,&a->kb_cnt))
+                       return -EFAULT;
+               if (ct >= MAX_DIACR)
+                       return -EINVAL;
+               accent_table_size = ct;
+               for (i = 0; i < ct; i++) {
+                       if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr)))
+                               return -EFAULT;
+                       accent_table[i].diacr = conv_8bit_to_uni(diacr.diacr);
+                       accent_table[i].base = conv_8bit_to_uni(diacr.base);
+                       accent_table[i].result = conv_8bit_to_uni(diacr.result);
+               }
+               return 0;
+       }
+
+       case KDSKBDIACRUC:
+       {
+               struct kbdiacrsuc __user *a = up;
                unsigned int ct;
 
                if (!perm)
@@ -602,7 +644,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                if (ct >= MAX_DIACR)
                        return -EINVAL;
                accent_table_size = ct;
-               if (copy_from_user(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr)))
+               if (copy_from_user(accent_table, a->kbdiacruc, ct*sizeof(struct kbdiacruc)))
                        return -EFAULT;
                return 0;
        }
index 564baca01b7c001635f9b6e4495f07468358c6e9..389346cda6c826fdecfb7d8663a7950ddd2fab43 100644 (file)
@@ -150,7 +150,7 @@ char *func_table[MAX_NR_FUNC] = {
        NULL,
 };
 
-struct kbdiacr accent_table[MAX_DIACR] = {
+struct kbdiacruc accent_table[MAX_DIACR] = {
        {'^', 'c', '\003'},     {'^', 'd', '\004'},
        {'^', 'z', '\032'},     {'^', '\012', '\000'},
 };
index f62f9a4e89504005f8d45fe0aa0e96df5d513645..cee4d4e4242907bb7a917070b0e3ac7b706cb794 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/sched.h>
 #include <linux/sysrq.h>
 
+#include <linux/consolemap.h>
 #include <linux/kbd_kern.h>
 #include <linux/kbd_diacr.h>
 #include <asm/uaccess.h>
@@ -82,11 +83,11 @@ kbd_alloc(void) {
        if (!kbd->fn_handler)
                goto out_func;
        kbd->accent_table =
-               kmalloc(sizeof(struct kbdiacr)*MAX_DIACR, GFP_KERNEL);
+               kmalloc(sizeof(struct kbdiacruc)*MAX_DIACR, GFP_KERNEL);
        if (!kbd->accent_table)
                goto out_fn_handler;
        memcpy(kbd->accent_table, accent_table,
-              sizeof(struct kbdiacr)*MAX_DIACR);
+              sizeof(struct kbdiacruc)*MAX_DIACR);
        kbd->accent_table_size = accent_table_size;
        return kbd;
 
@@ -183,8 +184,8 @@ kbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc)
  * Otherwise, conclude that DIACR was not combining after all,
  * queue it and return CH.
  */
-static unsigned char
-handle_diacr(struct kbd_data *kbd, unsigned char ch)
+static unsigned int
+handle_diacr(struct kbd_data *kbd, unsigned int ch)
 {
        int i, d;
 
@@ -460,7 +461,6 @@ int
 kbd_ioctl(struct kbd_data *kbd, struct file *file,
          unsigned int cmd, unsigned long arg)
 {
-       struct kbdiacrs __user *a;
        void __user *argp;
        int ct, perm;
 
@@ -481,17 +481,40 @@ kbd_ioctl(struct kbd_data *kbd, struct file *file,
        case KDSKBSENT:
                return do_kdgkb_ioctl(kbd, argp, cmd, perm);
        case KDGKBDIACR:
-               a = argp;
+       {
+               struct kbdiacrs __user *a = argp;
+               struct kbdiacr diacr;
+               int i;
 
                if (put_user(kbd->accent_table_size, &a->kb_cnt))
                        return -EFAULT;
+               for (i = 0; i < kbd->accent_table_size; i++) {
+                       diacr.diacr = kbd->accent_table[i].diacr;
+                       diacr.base = kbd->accent_table[i].base;
+                       diacr.result = kbd->accent_table[i].result;
+                       if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr)))
+                       return -EFAULT;
+               }
+               return 0;
+       }
+       case KDGKBDIACRUC:
+       {
+               struct kbdiacrsuc __user *a = argp;
+
                ct = kbd->accent_table_size;
-               if (copy_to_user(a->kbdiacr, kbd->accent_table,
-                                ct * sizeof(struct kbdiacr)))
+               if (put_user(ct, &a->kb_cnt))
+                       return -EFAULT;
+               if (copy_to_user(a->kbdiacruc, kbd->accent_table,
+                                ct * sizeof(struct kbdiacruc)))
                        return -EFAULT;
                return 0;
+       }
        case KDSKBDIACR:
-               a = argp;
+       {
+               struct kbdiacrs __user *a = argp;
+               struct kbdiacr diacr;
+               int i;
+
                if (!perm)
                        return -EPERM;
                if (get_user(ct, &a->kb_cnt))
@@ -499,10 +522,31 @@ kbd_ioctl(struct kbd_data *kbd, struct file *file,
                if (ct >= MAX_DIACR)
                        return -EINVAL;
                kbd->accent_table_size = ct;
-               if (copy_from_user(kbd->accent_table, a->kbdiacr,
-                                  ct * sizeof(struct kbdiacr)))
+               for (i = 0; i < ct; i++) {
+                       if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr)))
+                               return -EFAULT;
+                       kbd->accent_table[i].diacr = diacr.diacr;
+                       kbd->accent_table[i].base = diacr.base;
+                       kbd->accent_table[i].result = diacr.result;
+               }
+               return 0;
+       }
+       case KDSKBDIACRUC:
+       {
+               struct kbdiacrsuc __user *a = argp;
+
+               if (!perm)
+                       return -EPERM;
+               if (get_user(ct, &a->kb_cnt))
+                       return -EFAULT;
+               if (ct >= MAX_DIACR)
+                       return -EINVAL;
+               kbd->accent_table_size = ct;
+               if (copy_from_user(kbd->accent_table, a->kbdiacruc,
+                                  ct * sizeof(struct kbdiacruc)))
                        return -EFAULT;
                return 0;
+       }
        default:
                return -ENOIOCTLCMD;
        }
index f7bf45c6bf0de23899fb1ff7136fef865aa3d0f8..5ccfe9cf126d1c9f6b7ebb4f5cabccd075f0d524 100644 (file)
@@ -25,9 +25,9 @@ struct kbd_data {
        unsigned short **key_maps;
        char **func_table;
        fn_handler_fn **fn_handler;
-       struct kbdiacr *accent_table;
+       struct kbdiacruc *accent_table;
        unsigned int accent_table_size;
-       unsigned char diacr;
+       unsigned int diacr;
        unsigned short sysrq;
 };
 
index a9df8f5bf62b312da4854c6b5c479e28b2692001..4d2aba597343b88d4eff6f8e698e918716402850 100644 (file)
@@ -225,7 +225,7 @@ char *func_table[MAX_NR_FUNC] = {
        0,
 };
 
-struct kbdiacr accent_table[MAX_DIACR] = {
+struct kbdiacruc accent_table[MAX_DIACR] = {
        {'`', 'A', 'À'},        {'`', 'a', 'à'},
        {'\'', 'A', 'Á'},       {'\'', 'a', 'á'},
        {'^', 'A', 'Â'},        {'^', 'a', 'â'},
index 06b2768c603f6a9ff0aeb70ef050245698b5ee78..e2bf7e5db39a513c650e1e422f024f31c0e6ed2d 100644 (file)
@@ -16,4 +16,5 @@ extern u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode);
 extern unsigned short *set_translate(int m, struct vc_data *vc);
 extern int conv_uni_to_pc(struct vc_data *conp, long ucs);
 extern u32 conv_8bit_to_uni(unsigned char c);
+extern int conv_uni_to_8bit(u32 uni);
 void console_map_init(void);
index 1c1a3ff0535b9299315787b7fff7b3ba0965f0b8..7274ec68c246628bf5939f30f76ef450a3bb6483 100644 (file)
@@ -2,7 +2,7 @@
 #define _DIACR_H
 #include <linux/kd.h>
 
-extern struct kbdiacr accent_table[];
+extern struct kbdiacruc accent_table[];
 extern unsigned int accent_table_size;
 
 #endif /* _DIACR_H */
index 28be4fbe9044a988a16497746da884be5a5a22c6..c91fc0c9c4951418bc70b7cb8f957da08a9699c3 100644 (file)
@@ -125,6 +125,16 @@ struct kbdiacrs {
 #define KDGKBDIACR      0x4B4A  /* read kernel accent table */
 #define KDSKBDIACR      0x4B4B  /* write kernel accent table */
 
+struct kbdiacruc {
+        __u32 diacr, base, result;
+};
+struct kbdiacrsuc {
+        unsigned int kb_cnt;    /* number of entries in following array */
+       struct kbdiacruc kbdiacruc[256];    /* MAX_DIACR from keyboard.h */
+};
+#define KDGKBDIACRUC    0x4BFA  /* read kernel accent table - UCS */
+#define KDSKBDIACRUC    0x4BFB  /* write kernel accent table - UCS */
+
 struct kbkeycode {
        unsigned int scancode, keycode;
 };