blob: 060c69048c3ddc38fc867250f40c3dda66c7d1be [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 */
18static 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 */
31int 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
73static 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
93int 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;
209out:
210 *len_p = (char *)op - astr;
211 return res;
212}
213
214int 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}