vserver 2.0 rc7
[linux-2.6.git] / fs / hfsplus / unicode.c
index 4821cd2..060c690 100644 (file)
@@ -19,9 +19,9 @@ static inline u16 case_fold(u16 c)
 {
         u16 tmp;
 
-        tmp = case_fold_table[(c>>8)];
+        tmp = hfsplus_case_fold_table[c >> 8];
         if (tmp)
-                tmp = case_fold_table[tmp + (c & 0xFF)];
+                tmp = hfsplus_case_fold_table[tmp + (c & 0xff)];
         else
                 tmp = c;
         return tmp;
@@ -59,69 +59,175 @@ int hfsplus_unistrcmp(const struct hfsplus_unistr *s1, const struct hfsplus_unis
        }
 }
 
-int hfsplus_uni2asc(const struct hfsplus_unistr *ustr, char *astr, int *len)
+#define Hangul_SBase   0xac00
+#define Hangul_LBase   0x1100
+#define Hangul_VBase   0x1161
+#define Hangul_TBase   0x11a7
+#define Hangul_SCount  11172
+#define Hangul_LCount  19
+#define Hangul_VCount  21
+#define Hangul_TCount  28
+#define Hangul_NCount  (Hangul_VCount * Hangul_TCount)
+
+
+static u16 *hfsplus_compose_lookup(u16 *p, u16 cc)
+{
+       int i, s, e;
+
+       s = 1;
+       e = p[1];
+       if (!e || cc < p[s * 2] || cc > p[e * 2])
+               return NULL;
+       do {
+               i = (s + e) / 2;
+               if (cc > p[i * 2])
+                       s = i + 1;
+               else if (cc < p[i * 2])
+                       e = i - 1;
+               else
+                       return hfsplus_compose_table + p[i * 2 + 1];
+       } while (s <= e);
+       return NULL;
+}
+
+int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, char *astr, int *len_p)
 {
        const hfsplus_unichr *ip;
+       struct nls_table *nls = HFSPLUS_SB(sb).nls;
        u8 *op;
-       u16 ustrlen, cc;
-       int size, tmp;
+       u16 cc, c0, c1;
+       u16 *ce1, *ce2;
+       int i, len, ustrlen, res, compose;
 
        op = astr;
        ip = ustr->unicode;
        ustrlen = be16_to_cpu(ustr->length);
-       tmp = *len;
-       while (ustrlen > 0 && tmp > 0) {
-               cc = be16_to_cpu(*ip);
-               switch (cc) {
+       len = *len_p;
+       ce1 = NULL;
+       compose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE);
+
+       while (ustrlen > 0) {
+               c0 = be16_to_cpu(*ip++);
+               ustrlen--;
+               /* search for single decomposed char */
+               if (likely(compose))
+                       ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c0);
+               if (ce1 && (cc = ce1[0])) {
+                       /* start of a possibly decomposed Hangul char */
+                       if (cc != 0xffff)
+                               goto done;
+                       if (!ustrlen)
+                               goto same;
+                       c1 = be16_to_cpu(*ip) - Hangul_VBase;
+                       if (c1 < Hangul_VCount) {
+                               /* compose the Hangul char */
+                               cc = (c0 - Hangul_LBase) * Hangul_VCount;
+                               cc = (cc + c1) * Hangul_TCount;
+                               cc += Hangul_SBase;
+                               ip++;
+                               ustrlen--;
+                               if (!ustrlen)
+                                       goto done;
+                               c1 = be16_to_cpu(*ip) - Hangul_TBase;
+                               if (c1 > 0 && c1 < Hangul_TCount) {
+                                       cc += c1;
+                                       ip++;
+                                       ustrlen--;
+                               }
+                               goto done;
+                       }
+               }
+               while (1) {
+                       /* main loop for common case of not composed chars */
+                       if (!ustrlen)
+                               goto same;
+                       c1 = be16_to_cpu(*ip);
+                       if (likely(compose))
+                               ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c1);
+                       if (ce1)
+                               break;
+                       switch (c0) {
+                       case 0:
+                               c0 = 0x2400;
+                               break;
+                       case '/':
+                               c0 = ':';
+                               break;
+                       }
+                       res = nls->uni2char(c0, op, len);
+                       if (res < 0) {
+                               if (res == -ENAMETOOLONG)
+                                       goto out;
+                               *op = '?';
+                               res = 1;
+                       }
+                       op += res;
+                       len -= res;
+                       c0 = c1;
+                       ip++;
+                       ustrlen--;
+               }
+               ce2 = hfsplus_compose_lookup(ce1, c0);
+               if (ce2) {
+                       i = 1;
+                       while (i < ustrlen) {
+                               ce1 = hfsplus_compose_lookup(ce2, be16_to_cpu(ip[i]));
+                               if (!ce1)
+                                       break;
+                               i++;
+                               ce2 = ce1;
+                       }
+                       if ((cc = ce2[0])) {
+                               ip += i;
+                               ustrlen -= i;
+                               goto done;
+                       }
+               }
+       same:
+               switch (c0) {
                case 0:
                        cc = 0x2400;
                        break;
                case '/':
                        cc = ':';
                        break;
+               default:
+                       cc = c0;
                }
-               if (cc > 0x7f) {
-                       size = utf8_wctomb(op, cc, tmp);
-                       if (size == -1) {
-                               /* ignore */
-                       } else {
-                               op += size;
-                               tmp -= size;
-                       }
-               } else {
-                       *op++ = (u8) cc;
-                       tmp--;
+       done:
+               res = nls->uni2char(cc, op, len);
+               if (res < 0) {
+                       if (res == -ENAMETOOLONG)
+                               goto out;
+                       *op = '?';
+                       res = 1;
                }
-               ip++;
-               ustrlen--;
+               op += res;
+               len -= res;
        }
-       *len = (char *)op - astr;
-       if (ustrlen)
-               return -ENAMETOOLONG;
-       return 0;
+       res = 0;
+out:
+       *len_p = (char *)op - astr;
+       return res;
 }
 
-int hfsplus_asc2uni(struct hfsplus_unistr *ustr, const char *astr, int len)
+int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, const char *astr, int len)
 {
-       int tmp;
+       struct nls_table *nls = HFSPLUS_SB(sb).nls;
+       int size, off, decompose;
        wchar_t c;
        u16 outlen = 0;
 
-       while (outlen <= HFSPLUS_MAX_STRLEN && len > 0) {
-               if (*astr & 0x80) {
-                       tmp = utf8_mbtowc(&c, astr, len);
-                       if (tmp < 0) {
-                               astr++;
-                               len--;
-                               continue;
-                       } else {
-                               astr += tmp;
-                               len -= tmp;
-                       }
-               } else {
-                       c = *astr++;
-                       len--;
+       decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE);
+
+       while (outlen < HFSPLUS_MAX_STRLEN && len > 0) {
+               size = nls->char2uni(astr, len, &c);
+               if (size <= 0) {
+                       c = '?';
+                       size = 1;
                }
+               astr += size;
+               len -= size;
                switch (c) {
                case 0x2400:
                        c = 0;
@@ -130,8 +236,33 @@ int hfsplus_asc2uni(struct hfsplus_unistr *ustr, const char *astr, int len)
                        c = '/';
                        break;
                }
-               ustr->unicode[outlen] = cpu_to_be16(c);
-               outlen++;
+               if (c >= 0xc0 && decompose) {
+                       off = hfsplus_decompose_table[(c >> 12) & 0xf];
+                       if (!off)
+                               goto done;
+                       if (off == 0xffff) {
+                               goto done;
+                       }
+                       off = hfsplus_decompose_table[off + ((c >> 8) & 0xf)];
+                       if (!off)
+                               goto done;
+                       off = hfsplus_decompose_table[off + ((c >> 4) & 0xf)];
+                       if (!off)
+                               goto done;
+                       off = hfsplus_decompose_table[off + (c & 0xf)];
+                       size = off & 3;
+                       if (!size)
+                               goto done;
+                       off /= 4;
+                       if (outlen + size > HFSPLUS_MAX_STRLEN)
+                               break;
+                       do {
+                               ustr->unicode[outlen++] = cpu_to_be16(hfsplus_decompose_table[off++]);
+                       } while (--size > 0);
+                       continue;
+               }
+       done:
+               ustr->unicode[outlen++] = cpu_to_be16(c);
        }
        ustr->length = cpu_to_be16(outlen);
        if (len > 0)