xref: /openbmc/linux/fs/hfsplus/unicode.c (revision d5cb9783536a41df9f9cba5b0a1d78047ed787f7)
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