Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / fs / hfsplus / unicode.c
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 }