1 /* 2 * linux/fs/hfsplus/unicode.c 3 * 4 * Copyright (C) 2001 5 * Brad Boyer (flar@allandria.com) 6 * (C) 2003 Ardis Technologies <roman@ardistech.com> 7 * 8 * Handler routines for unicode strings 9 */ 10 11 #include <linux/types.h> 12 #include <linux/nls.h> 13 #include "hfsplus_fs.h" 14 #include "hfsplus_raw.h" 15 16 /* Fold the case of a unicode char, given the 16 bit value */ 17 /* Returns folded char, or 0 if ignorable */ 18 static inline u16 case_fold(u16 c) 19 { 20 u16 tmp; 21 22 tmp = hfsplus_case_fold_table[c >> 8]; 23 if (tmp) 24 tmp = hfsplus_case_fold_table[tmp + (c & 0xff)]; 25 else 26 tmp = c; 27 return tmp; 28 } 29 30 /* Compare unicode strings, return values like normal strcmp */ 31 int hfsplus_unistrcmp(const struct hfsplus_unistr *s1, const struct hfsplus_unistr *s2) 32 { 33 u16 len1, len2, c1, c2; 34 const hfsplus_unichr *p1, *p2; 35 36 len1 = be16_to_cpu(s1->length); 37 len2 = be16_to_cpu(s2->length); 38 p1 = s1->unicode; 39 p2 = s2->unicode; 40 41 while (1) { 42 c1 = c2 = 0; 43 44 while (len1 && !c1) { 45 c1 = case_fold(be16_to_cpu(*p1)); 46 p1++; 47 len1--; 48 } 49 while (len2 && !c2) { 50 c2 = case_fold(be16_to_cpu(*p2)); 51 p2++; 52 len2--; 53 } 54 55 if (c1 != c2) 56 return (c1 < c2) ? -1 : 1; 57 if (!c1 && !c2) 58 return 0; 59 } 60 } 61 62 #define Hangul_SBase 0xac00 63 #define Hangul_LBase 0x1100 64 #define Hangul_VBase 0x1161 65 #define Hangul_TBase 0x11a7 66 #define Hangul_SCount 11172 67 #define Hangul_LCount 19 68 #define Hangul_VCount 21 69 #define Hangul_TCount 28 70 #define Hangul_NCount (Hangul_VCount * Hangul_TCount) 71 72 73 static u16 *hfsplus_compose_lookup(u16 *p, u16 cc) 74 { 75 int i, s, e; 76 77 s = 1; 78 e = p[1]; 79 if (!e || cc < p[s * 2] || cc > p[e * 2]) 80 return NULL; 81 do { 82 i = (s + e) / 2; 83 if (cc > p[i * 2]) 84 s = i + 1; 85 else if (cc < p[i * 2]) 86 e = i - 1; 87 else 88 return hfsplus_compose_table + p[i * 2 + 1]; 89 } while (s <= e); 90 return NULL; 91 } 92 93 int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, char *astr, int *len_p) 94 { 95 const hfsplus_unichr *ip; 96 struct nls_table *nls = HFSPLUS_SB(sb).nls; 97 u8 *op; 98 u16 cc, c0, c1; 99 u16 *ce1, *ce2; 100 int i, len, ustrlen, res, compose; 101 102 op = astr; 103 ip = ustr->unicode; 104 ustrlen = be16_to_cpu(ustr->length); 105 len = *len_p; 106 ce1 = NULL; 107 compose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); 108 109 while (ustrlen > 0) { 110 c0 = be16_to_cpu(*ip++); 111 ustrlen--; 112 /* search for single decomposed char */ 113 if (likely(compose)) 114 ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c0); 115 if (ce1 && (cc = ce1[0])) { 116 /* start of a possibly decomposed Hangul char */ 117 if (cc != 0xffff) 118 goto done; 119 if (!ustrlen) 120 goto same; 121 c1 = be16_to_cpu(*ip) - Hangul_VBase; 122 if (c1 < Hangul_VCount) { 123 /* compose the Hangul char */ 124 cc = (c0 - Hangul_LBase) * Hangul_VCount; 125 cc = (cc + c1) * Hangul_TCount; 126 cc += Hangul_SBase; 127 ip++; 128 ustrlen--; 129 if (!ustrlen) 130 goto done; 131 c1 = be16_to_cpu(*ip) - Hangul_TBase; 132 if (c1 > 0 && c1 < Hangul_TCount) { 133 cc += c1; 134 ip++; 135 ustrlen--; 136 } 137 goto done; 138 } 139 } 140 while (1) { 141 /* main loop for common case of not composed chars */ 142 if (!ustrlen) 143 goto same; 144 c1 = be16_to_cpu(*ip); 145 if (likely(compose)) 146 ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c1); 147 if (ce1) 148 break; 149 switch (c0) { 150 case 0: 151 c0 = 0x2400; 152 break; 153 case '/': 154 c0 = ':'; 155 break; 156 } 157 res = nls->uni2char(c0, op, len); 158 if (res < 0) { 159 if (res == -ENAMETOOLONG) 160 goto out; 161 *op = '?'; 162 res = 1; 163 } 164 op += res; 165 len -= res; 166 c0 = c1; 167 ip++; 168 ustrlen--; 169 } 170 ce2 = hfsplus_compose_lookup(ce1, c0); 171 if (ce2) { 172 i = 1; 173 while (i < ustrlen) { 174 ce1 = hfsplus_compose_lookup(ce2, be16_to_cpu(ip[i])); 175 if (!ce1) 176 break; 177 i++; 178 ce2 = ce1; 179 } 180 if ((cc = ce2[0])) { 181 ip += i; 182 ustrlen -= i; 183 goto done; 184 } 185 } 186 same: 187 switch (c0) { 188 case 0: 189 cc = 0x2400; 190 break; 191 case '/': 192 cc = ':'; 193 break; 194 default: 195 cc = c0; 196 } 197 done: 198 res = nls->uni2char(cc, op, len); 199 if (res < 0) { 200 if (res == -ENAMETOOLONG) 201 goto out; 202 *op = '?'; 203 res = 1; 204 } 205 op += res; 206 len -= res; 207 } 208 res = 0; 209 out: 210 *len_p = (char *)op - astr; 211 return res; 212 } 213 214 int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, const char *astr, int len) 215 { 216 struct nls_table *nls = HFSPLUS_SB(sb).nls; 217 int size, off, decompose; 218 wchar_t c; 219 u16 outlen = 0; 220 221 decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); 222 223 while (outlen < HFSPLUS_MAX_STRLEN && len > 0) { 224 size = nls->char2uni(astr, len, &c); 225 if (size <= 0) { 226 c = '?'; 227 size = 1; 228 } 229 astr += size; 230 len -= size; 231 switch (c) { 232 case 0x2400: 233 c = 0; 234 break; 235 case ':': 236 c = '/'; 237 break; 238 } 239 if (c >= 0xc0 && decompose) { 240 off = hfsplus_decompose_table[(c >> 12) & 0xf]; 241 if (!off) 242 goto done; 243 if (off == 0xffff) { 244 goto done; 245 } 246 off = hfsplus_decompose_table[off + ((c >> 8) & 0xf)]; 247 if (!off) 248 goto done; 249 off = hfsplus_decompose_table[off + ((c >> 4) & 0xf)]; 250 if (!off) 251 goto done; 252 off = hfsplus_decompose_table[off + (c & 0xf)]; 253 size = off & 3; 254 if (!size) 255 goto done; 256 off /= 4; 257 if (outlen + size > HFSPLUS_MAX_STRLEN) 258 break; 259 do { 260 ustr->unicode[outlen++] = cpu_to_be16(hfsplus_decompose_table[off++]); 261 } while (--size > 0); 262 continue; 263 } 264 done: 265 ustr->unicode[outlen++] = cpu_to_be16(c); 266 } 267 ustr->length = cpu_to_be16(outlen); 268 if (len > 0) 269 return -ENAMETOOLONG; 270 return 0; 271 } 272