• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2011-2012 - Mauro Carvalho Chehab
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation version 2.1 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16  * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
17  */
18 
19 /*
20  * handle character set correctly (e.g. via iconv)
21  *   c.f. EN 300 468 annex A
22  */
23 
24 #include <iconv.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <strings.h> /* strcasecmp */
29 
30 #include <parse_string.h>
31 #include <libdvbv5/dvb-log.h>
32 #include <libdvbv5/dvb-fe.h>
33 
34 #define CS_OPTIONS "//TRANSLIT"
35 
36 struct charset_conv {
37 	unsigned len;
38 	unsigned char  data[3];
39 };
40 
41 /* This table is the Latin 00 table. Basically ISO-6937 + Euro sign */
42 static struct charset_conv en300468_latin_00_to_utf8[256] = {
43 	[0x00] = { 1, {0x00, } },
44 	[0x01] = { 1, {0x01, } },
45 	[0x02] = { 1, {0x02, } },
46 	[0x03] = { 1, {0x03, } },
47 	[0x04] = { 1, {0x04, } },
48 	[0x05] = { 1, {0x05, } },
49 	[0x06] = { 1, {0x06, } },
50 	[0x07] = { 1, {0x07, } },
51 	[0x08] = { 1, {0x08, } },
52 	[0x09] = { 1, {0x09, } },
53 	[0x0a] = { 1, {0x0a, } },
54 	[0x0b] = { 1, {0x0b, } },
55 	[0x0c] = { 1, {0x0c, } },
56 	[0x0d] = { 1, {0x0d, } },
57 	[0x0e] = { 1, {0x0e, } },
58 	[0x0f] = { 1, {0x0f, } },
59 	[0x10] = { 1, {0x10, } },
60 	[0x11] = { 1, {0x11, } },
61 	[0x12] = { 1, {0x12, } },
62 	[0x13] = { 1, {0x13, } },
63 	[0x14] = { 1, {0x14, } },
64 	[0x15] = { 1, {0x15, } },
65 	[0x16] = { 1, {0x16, } },
66 	[0x17] = { 1, {0x17, } },
67 	[0x18] = { 1, {0x18, } },
68 	[0x19] = { 1, {0x19, } },
69 	[0x1a] = { 1, {0x1a, } },
70 	[0x1b] = { 1, {0x1b, } },
71 	[0x1c] = { 1, {0x1c, } },
72 	[0x1d] = { 1, {0x1d, } },
73 	[0x1e] = { 1, {0x1e, } },
74 	[0x1f] = { 1, {0x1f, } },
75 	[0x20] = { 1, {0x20, } },
76 	[0x21] = { 1, {0x21, } },
77 	[0x22] = { 1, {0x22, } },
78 	[0x23] = { 1, {0x23, } },
79 	[0x24] = { 1, {0x24, } },
80 	[0x25] = { 1, {0x25, } },
81 	[0x26] = { 1, {0x26, } },
82 	[0x27] = { 1, {0x27, } },
83 	[0x28] = { 1, {0x28, } },
84 	[0x29] = { 1, {0x29, } },
85 	[0x2a] = { 1, {0x2a, } },
86 	[0x2b] = { 1, {0x2b, } },
87 	[0x2c] = { 1, {0x2c, } },
88 	[0x2d] = { 1, {0x2d, } },
89 	[0x2e] = { 1, {0x2e, } },
90 	[0x2f] = { 1, {0x2f, } },
91 	[0x30] = { 1, {0x30, } },
92 	[0x31] = { 1, {0x31, } },
93 	[0x32] = { 1, {0x32, } },
94 	[0x33] = { 1, {0x33, } },
95 	[0x34] = { 1, {0x34, } },
96 	[0x35] = { 1, {0x35, } },
97 	[0x36] = { 1, {0x36, } },
98 	[0x37] = { 1, {0x37, } },
99 	[0x38] = { 1, {0x38, } },
100 	[0x39] = { 1, {0x39, } },
101 	[0x3a] = { 1, {0x3a, } },
102 	[0x3b] = { 1, {0x3b, } },
103 	[0x3c] = { 1, {0x3c, } },
104 	[0x3d] = { 1, {0x3d, } },
105 	[0x3e] = { 1, {0x3e, } },
106 	[0x3f] = { 1, {0x3f, } },
107 	[0x40] = { 1, {0x40, } },
108 	[0x41] = { 1, {0x41, } },
109 	[0x42] = { 1, {0x42, } },
110 	[0x43] = { 1, {0x43, } },
111 	[0x44] = { 1, {0x44, } },
112 	[0x45] = { 1, {0x45, } },
113 	[0x46] = { 1, {0x46, } },
114 	[0x47] = { 1, {0x47, } },
115 	[0x48] = { 1, {0x48, } },
116 	[0x49] = { 1, {0x49, } },
117 	[0x4a] = { 1, {0x4a, } },
118 	[0x4b] = { 1, {0x4b, } },
119 	[0x4c] = { 1, {0x4c, } },
120 	[0x4d] = { 1, {0x4d, } },
121 	[0x4e] = { 1, {0x4e, } },
122 	[0x4f] = { 1, {0x4f, } },
123 	[0x50] = { 1, {0x50, } },
124 	[0x51] = { 1, {0x51, } },
125 	[0x52] = { 1, {0x52, } },
126 	[0x53] = { 1, {0x53, } },
127 	[0x54] = { 1, {0x54, } },
128 	[0x55] = { 1, {0x55, } },
129 	[0x56] = { 1, {0x56, } },
130 	[0x57] = { 1, {0x57, } },
131 	[0x58] = { 1, {0x58, } },
132 	[0x59] = { 1, {0x59, } },
133 	[0x5a] = { 1, {0x5a, } },
134 	[0x5b] = { 1, {0x5b, } },
135 	[0x5c] = { 1, {0x5c, } },
136 	[0x5d] = { 1, {0x5d, } },
137 	[0x5e] = { 1, {0x5e, } },
138 	[0x5f] = { 1, {0x5f, } },
139 	[0x60] = { 1, {0x60, } },
140 	[0x61] = { 1, {0x61, } },
141 	[0x62] = { 1, {0x62, } },
142 	[0x63] = { 1, {0x63, } },
143 	[0x64] = { 1, {0x64, } },
144 	[0x65] = { 1, {0x65, } },
145 	[0x66] = { 1, {0x66, } },
146 	[0x67] = { 1, {0x67, } },
147 	[0x68] = { 1, {0x68, } },
148 	[0x69] = { 1, {0x69, } },
149 	[0x6a] = { 1, {0x6a, } },
150 	[0x6b] = { 1, {0x6b, } },
151 	[0x6c] = { 1, {0x6c, } },
152 	[0x6d] = { 1, {0x6d, } },
153 	[0x6e] = { 1, {0x6e, } },
154 	[0x6f] = { 1, {0x6f, } },
155 	[0x70] = { 1, {0x70, } },
156 	[0x71] = { 1, {0x71, } },
157 	[0x72] = { 1, {0x72, } },
158 	[0x73] = { 1, {0x73, } },
159 	[0x74] = { 1, {0x74, } },
160 	[0x75] = { 1, {0x75, } },
161 	[0x76] = { 1, {0x76, } },
162 	[0x77] = { 1, {0x77, } },
163 	[0x78] = { 1, {0x78, } },
164 	[0x79] = { 1, {0x79, } },
165 	[0x7a] = { 1, {0x7a, } },
166 	[0x7b] = { 1, {0x7b, } },
167 	[0x7c] = { 1, {0x7c, } },
168 	[0x7d] = { 1, {0x7d, } },
169 	[0x7e] = { 1, {0x7e, } },
170 	[0x7f] = { 1, {0x7f, } },
171 	[0x80] = { 2, {0xc2, 0x80, } },
172 	[0x81] = { 2, {0xc2, 0x81, } },
173 	[0x82] = { 2, {0xc2, 0x82, } },
174 	[0x83] = { 2, {0xc2, 0x83, } },
175 	[0x84] = { 2, {0xc2, 0x84, } },
176 	[0x85] = { 2, {0xc2, 0x85, } },
177 	[0x86] = { 2, {0xc2, 0x86, } },
178 	[0x87] = { 2, {0xc2, 0x87, } },
179 	[0x88] = { 2, {0xc2, 0x88, } },
180 	[0x89] = { 2, {0xc2, 0x89, } },
181 	[0x8a] = { 2, {0xc2, 0x8a, } },
182 	[0x8b] = { 2, {0xc2, 0x8b, } },
183 	[0x8c] = { 2, {0xc2, 0x8c, } },
184 	[0x8d] = { 2, {0xc2, 0x8d, } },
185 	[0x8e] = { 2, {0xc2, 0x8e, } },
186 	[0x8f] = { 2, {0xc2, 0x8f, } },
187 	[0x90] = { 2, {0xc2, 0x90, } },
188 	[0x91] = { 2, {0xc2, 0x91, } },
189 	[0x92] = { 2, {0xc2, 0x92, } },
190 	[0x93] = { 2, {0xc2, 0x93, } },
191 	[0x94] = { 2, {0xc2, 0x94, } },
192 	[0x95] = { 2, {0xc2, 0x95, } },
193 	[0x96] = { 2, {0xc2, 0x96, } },
194 	[0x97] = { 2, {0xc2, 0x97, } },
195 	[0x98] = { 2, {0xc2, 0x98, } },
196 	[0x99] = { 2, {0xc2, 0x99, } },
197 	[0x9a] = { 2, {0xc2, 0x9a, } },
198 	[0x9b] = { 2, {0xc2, 0x9b, } },
199 	[0x9c] = { 2, {0xc2, 0x9c, } },
200 	[0x9d] = { 2, {0xc2, 0x9d, } },
201 	[0x9e] = { 2, {0xc2, 0x9e, } },
202 	[0x9f] = { 2, {0xc2, 0x9f, } },
203 	[0xa0] = { 2, {0xc2, 0xa0, } },
204 	[0xa1] = { 2, {0xc2, 0xa1, } },
205 	[0xa2] = { 2, {0xc2, 0xa2, } },
206 	[0xa3] = { 2, {0xc2, 0xa3, } },
207 	[0xa4] = { 3, { 0xe2, 0x82, 0xac,} },		/* Euro sign. Addition over the ISO-6937 standard */
208 	[0xa5] = { 2, {0xc2, 0xa5, } },
209 	[0xa6] = { 0, {} },
210 	[0xa7] = { 2, {0xc2, 0xa7, } },
211 	[0xa8] = { 2, {0xc2, 0xa4, } },
212 	[0xa9] = { 3, {0xe2, 0x80, 0x98, } },
213 	[0xaa] = { 3, {0xe2, 0x80, 0x9c, } },
214 	[0xab] = { 2, {0xc2, 0xab, } },
215 	[0xac] = { 3, {0xe2, 0x86, 0x90, } },
216 	[0xad] = { 3, {0xe2, 0x86, 0x91, } },
217 	[0xae] = { 3, {0xe2, 0x86, 0x92, } },
218 	[0xaf] = { 3, {0xe2, 0x86, 0x93, } },
219 	[0xb0] = { 2, {0xc2, 0xb0, } },
220 	[0xb1] = { 2, {0xc2, 0xb1, } },
221 	[0xb2] = { 2, {0xc2, 0xb2, } },
222 	[0xb3] = { 2, {0xc2, 0xb3, } },
223 	[0xb4] = { 2, {0xc3, 0x97, } },
224 	[0xb5] = { 2, {0xc2, 0xb5, } },
225 	[0xb6] = { 2, {0xc2, 0xb6, } },
226 	[0xb7] = { 2, {0xc2, 0xb7, } },
227 	[0xb8] = { 2, {0xc3, 0xb7, } },
228 	[0xb9] = { 3, {0xe2, 0x80, 0x99, } },
229 	[0xba] = { 3, {0xe2, 0x80, 0x9d, } },
230 	[0xbb] = { 2, {0xc2, 0xbb, } },
231 	[0xbc] = { 2, {0xc2, 0xbc, } },
232 	[0xbd] = { 2, {0xc2, 0xbd, } },
233 	[0xbe] = { 2, {0xc2, 0xbe, } },
234 	[0xbf] = { 2, {0xc2, 0xbf, } },
235 	[0xc0] = { 0, {} },
236 	[0xc1] = { 0, {} },
237 	[0xc2] = { 0, {} },
238 	[0xc3] = { 0, {} },
239 	[0xc4] = { 0, {} },
240 	[0xc5] = { 0, {} },
241 	[0xc6] = { 0, {} },
242 	[0xc7] = { 0, {} },
243 	[0xc8] = { 0, {} },
244 	[0xc9] = { 0, {} },
245 	[0xca] = { 0, {} },
246 	[0xcb] = { 0, {} },
247 	[0xcc] = { 0, {} },
248 	[0xcd] = { 0, {} },
249 	[0xce] = { 0, {} },
250 	[0xcf] = { 0, {} },
251 	[0xd0] = { 3, {0xe2, 0x80, 0x94, } },
252 	[0xd1] = { 2, {0xc2, 0xb9, } },
253 	[0xd2] = { 2, {0xc2, 0xae, } },
254 	[0xd3] = { 2, {0xc2, 0xa9, } },
255 	[0xd4] = { 3, {0xe2, 0x84, 0xa2, } },
256 	[0xd5] = { 3, {0xe2, 0x99, 0xaa, } },
257 	[0xd6] = { 2, {0xc2, 0xac, } },
258 	[0xd7] = { 2, {0xc2, 0xa6, } },
259 	[0xd8] = { 0, {} },
260 	[0xd9] = { 0, {} },
261 	[0xda] = { 0, {} },
262 	[0xdb] = { 0, {} },
263 	[0xdc] = { 3, {0xe2, 0x85, 0x9b, } },
264 	[0xdd] = { 3, {0xe2, 0x85, 0x9c, } },
265 	[0xde] = { 3, {0xe2, 0x85, 0x9d, } },
266 	[0xdf] = { 3, {0xe2, 0x85, 0x9e, } },
267 	[0xe0] = { 3, {0xe2, 0x84, 0xa6, } },
268 	[0xe1] = { 2, {0xc3, 0x86, } },
269 	[0xe2] = { 2, {0xc3, 0x90, } },
270 	[0xe3] = { 2, {0xc2, 0xaa, } },
271 	[0xe4] = { 2, {0xc4, 0xa6, } },
272 	[0xe5] = { 0, {} },
273 	[0xe6] = { 2, {0xc4, 0xb2, } },
274 	[0xe7] = { 2, {0xc4, 0xbf, } },
275 	[0xe8] = { 2, {0xc5, 0x81, } },
276 	[0xe9] = { 2, {0xc3, 0x98, } },
277 	[0xea] = { 2, {0xc5, 0x92, } },
278 	[0xeb] = { 2, {0xc2, 0xba, } },
279 	[0xec] = { 2, {0xc3, 0x9e, } },
280 	[0xed] = { 2, {0xc5, 0xa6, } },
281 	[0xee] = { 2, {0xc5, 0x8a, } },
282 	[0xef] = { 2, {0xc5, 0x89, } },
283 	[0xf0] = { 2, {0xc4, 0xb8, } },
284 	[0xf1] = { 2, {0xc3, 0xa6, } },
285 	[0xf2] = { 2, {0xc4, 0x91, } },
286 	[0xf3] = { 2, {0xc3, 0xb0, } },
287 	[0xf4] = { 2, {0xc4, 0xa7, } },
288 	[0xf5] = { 2, {0xc4, 0xb1, } },
289 	[0xf6] = { 2, {0xc4, 0xb3, } },
290 	[0xf7] = { 2, {0xc5, 0x80, } },
291 	[0xf8] = { 2, {0xc5, 0x82, } },
292 	[0xf9] = { 2, {0xc3, 0xb8, } },
293 	[0xfa] = { 2, {0xc5, 0x93, } },
294 	[0xfb] = { 2, {0xc3, 0x9f, } },
295 	[0xfc] = { 2, {0xc3, 0xbe, } },
296 	[0xfd] = { 2, {0xc5, 0xa7, } },
297 	[0xfe] = { 2, {0xc5, 0x8b, } },
298 	[0xff] = { 2, {0xc2, 0xad, } },
299 };
300 
dvb_iconv_to_charset(struct dvb_v5_fe_parms * parms,char * dest,size_t destlen,const unsigned char * src,size_t len,char * input_charset,char * output_charset)301 void dvb_iconv_to_charset(struct dvb_v5_fe_parms *parms,
302 			  char *dest,
303 			  size_t destlen,
304 			  const unsigned char *src,
305 			  size_t len,
306 			  char *input_charset, char *output_charset)
307 {
308 	char out_cs[strlen(output_charset) + 1 + sizeof(CS_OPTIONS)];
309 	char *p = dest;
310 
311 	strcpy(out_cs, output_charset);
312 	strcat(out_cs, CS_OPTIONS);
313 
314 	iconv_t cd = iconv_open(out_cs, input_charset);
315 	if (cd == (iconv_t)(-1)) {
316 		memcpy(p, src, len);
317 		p[len] = '\0';
318 		dvb_logerr("Conversion from %s to %s not supported\n",
319 				input_charset, output_charset);
320 		if (!strcasecmp(input_charset, "ARIB-STD-B24"))
321 			dvb_log("Try setting GCONV_PATH to the bundled gconv dir.\n");
322 	} else {
323 		iconv(cd, (ICONV_CONST char **)&src, &len, &p, &destlen);
324 		iconv_close(cd);
325 		*p = '\0';
326 	}
327 }
328 
charset_conversion(struct dvb_v5_fe_parms * parms,char ** dest,const unsigned char * s,size_t len,char * input_charset)329 static void charset_conversion(struct dvb_v5_fe_parms *parms, char **dest, const unsigned char *s,
330 			       size_t len, char *input_charset)
331 {
332 	size_t destlen = len * 3;
333 	int need_conversion = 1;
334 
335 	/* Special handler for ISO-6937 */
336 	if (!strcasecmp(input_charset, "ISO-6937")) {
337 		char *p = *dest;
338 		unsigned char *tmp;
339 		unsigned char *p1, *p2;
340 
341 		/* Convert charset to UTF-8 using Code table 00 - Latin */
342 		for (p1 = (unsigned char *)s; p1 < s + len; p1++)
343 			for (p2 = en300468_latin_00_to_utf8[*p1].data;
344 			     p2 < en300468_latin_00_to_utf8[*p1].data + en300468_latin_00_to_utf8[*p1].len;
345 			     p2++)
346 				*p++ = *p2;
347 		*p = '\0';
348 
349 		/* If desired charset is not UTF-8, prepare for conversion */
350 		if (strcasecmp(parms->output_charset, "UTF-8")) {
351 			tmp = (unsigned char *)*dest;
352 			len = p - *dest;
353 
354 			*dest = malloc(destlen + 1);
355 			input_charset = "UTF-8";
356 			s = tmp;
357 		} else
358 			need_conversion = 0;
359 
360 	}
361 
362 	/* Convert from original charset to the desired one */
363 	if (need_conversion)
364 		dvb_iconv_to_charset(parms, *dest, destlen, s, len,
365 				     input_charset,
366 				     parms->output_charset);
367 }
368 
dvb_parse_string(struct dvb_v5_fe_parms * parms,char ** dest,char ** emph,const unsigned char * src,size_t len)369 void dvb_parse_string(struct dvb_v5_fe_parms *parms, char **dest, char **emph,
370 		      const unsigned char *src, size_t len)
371 {
372 	size_t destlen, i, len2 = 0;
373 	char *p, *p2, *type = parms->default_charset;
374 	unsigned char *tmp1 = NULL, *tmp2 = NULL;
375 	const unsigned char *s;
376 	int emphasis = 0;
377 
378 	if (*dest) {
379 		free(*dest);
380 		*dest = NULL;
381 	}
382 	if (*emph) {
383 		free(*emph);
384 		*emph = NULL;
385 	}
386 	if (!len)
387 		return;
388 
389 	/*
390 	 * Strings in ISDB-S/T(JP) do not start with a charset identifier,
391 	 * and can start with a control character (< 0x20).
392 	 */
393 	if (strcasecmp(type, "ARIB-STD-B24") && *src < 0x20) {
394 		switch (*src) {
395 		case 0x00:	type = "ISO-6937";		break;
396 		case 0x01:	type = "ISO-8859-5";		break;
397 		case 0x02:	type = "ISO-8859-6";		break;
398 		case 0x03:	type = "ISO-8859-7";		break;
399 		case 0x04:	type = "ISO-8859-8";		break;
400 		case 0x05:	type = "ISO-8859-9";		break;
401 		case 0x06:	type = "ISO-8859-10";		break;
402 		case 0x07:	type = "ISO-8859-11";		break;
403 		case 0x09:	type = "ISO-8859-13";		break;
404 		case 0x0a:	type = "ISO-8859-14";		break;
405 		case 0x0b:	type = "ISO-8859-15";		break;
406 		case 0x11:	type = "ISO-10646/UCS2";	break;
407 		case 0x12:	type = "ISO-2022-KR";		break;
408 		case 0x13:	type = "GB2312";		break;
409 		case 0x14:	type = "UTF-16BE";		break;
410 		case 0x15:	type = "ISO-10646/UTF-8";	break;
411 		case 0x10: /* ISO8859 */
412 			if (len < 2)
413 				break;
414 			if ((*(src + 1) != 0) || *(src + 2) > 0x0f)
415 				break;
416 			src+=2;
417 			len-=2;
418 			switch(*src) {
419 			case 0x01:	type = "ISO-8859-1";		break;
420 			case 0x02:	type = "ISO-8859-2";		break;
421 			case 0x03:	type = "ISO-8859-3";		break;
422 			case 0x04:	type = "ISO-8859-4";		break;
423 			case 0x05:	type = "ISO-8859-5";		break;
424 			case 0x06:	type = "ISO-8859-6";		break;
425 			case 0x07:	type = "ISO-8859-7";		break;
426 			case 0x08:	type = "ISO-8859-8";		break;
427 			case 0x09:	type = "ISO-8859-9";		break;
428 			case 0x0a:	type = "ISO-8859-10";		break;
429 			case 0x0b:	type = "ISO-8859-11";		break;
430 			case 0x0d:	type = "ISO-8859-13";		break;
431 			case 0x0e:	type = "ISO-8859-14";		break;
432 			case 0x0f:	type = "ISO-8859-15";		break;
433 			}
434 		}
435 		src++;
436 		len--;
437 	}
438 
439 	/*
440 	 * Destination length should be bigger. As the worse case seems to
441 	 * use 3 chars for one code, use it for destlen
442 	 */
443 	destlen = len * 3;
444 	*dest = malloc(destlen + 1);
445 	*emph = malloc(destlen + 1);
446 
447 	/* Remove special chars */
448 	if (!strncasecmp(type, "ISO-8859", 8) || !strcasecmp(type, "ISO-6937") || !strcasecmp(type, "ISO-10646/UTF-8")) {
449 		/*
450 		 * Handles the ISO/IEC 10646 1-byte control codes
451 		 * (EN 300 468 v1.11.1 Table A.1)
452 		 */
453 		tmp1 = malloc(len + 2);
454 		tmp2 = malloc(len + 2);
455 		p = (char *)tmp1;
456 		p2 = (char *)tmp2;
457 		s = src;
458 		for (i = 0; i < len; i++, s++) {
459 			if (*s == 0x86)
460 				emphasis = 1;
461 			else if (*s == 0x87 && emphasis)
462 				emphasis = 0;
463 			else if (*s >= 0x20 && (*s < 0x80 || *s > 0x9f)) {
464 				*p++ = *s;
465 				if (emphasis)
466 					*p2++ = *s;
467 			}
468 			else if (*s == 0x8a)
469 				*p++ = '\n';
470 		}
471 		*p = '\0';
472 		*p2 = '\0';
473 		len = p - (char *)tmp1;
474 		len2 = p2 - (char *)tmp2;
475 	} else if (!strcasecmp(type, "ISO-10646/UCS2")) {
476 		/*
477 		 * Handles the ISO/IEC 10646 2-bytes control codes
478 		 * (EN 300 468 v1.11.1 Table A.2)
479 		 */
480 		uint16_t *in_code = (void *)src;
481 		uint16_t *out_code;
482 		uint16_t *out_emph;
483 
484 		tmp1 = malloc(len + 2);
485 		tmp2 = malloc(len + 2);
486 		out_code = (void *)tmp1;
487 		out_emph = (void *)tmp2;
488 
489 		for (i = 0; i < len / 2; i ++, in_code++) {
490 			uint16_t code = *in_code;
491 
492 			/*
493 			 * FIXME: should it do bswap16(code) here?
494 			 */
495 			if (code == 0xe086)
496 				emphasis = 1;
497 			else if (code == 0xe087 && emphasis)
498 				emphasis = 0;
499 			else if (code == 0xe08a)
500 				/* newline, append code blow */ ;
501 			else if (code >= 0xe080 && code <= 0xe09f)
502 				continue;
503 
504 			*out_code++ = code;
505 			if (emphasis)
506 				*out_emph++ = code;
507 		}
508 		*out_code = 0;
509 		*out_emph = 0;
510 		len = (char *)out_code - (char *)tmp1;
511 		len2 = (char *)out_emph - (char *)tmp2;
512 	}
513 
514 	if (tmp1)
515 		s = tmp1;
516 	else
517 		s = src;
518 
519 	charset_conversion(parms, dest, s, len, type);
520 	/* The code had over-sized the space. Fix it. */
521 	if (*dest)
522 		*dest = realloc(*dest, strlen(*dest) + 1);
523 
524 	if (!len2) {
525 		if (tmp2) {
526 			free (tmp2);
527 			tmp2 = NULL;
528 		}
529 		free (*emph);
530 		*emph = NULL;
531 	} else {
532 		charset_conversion(parms, emph, tmp2, len2, type);
533 		*emph = realloc(*emph, strlen(*emph) + 1);
534 	}
535 
536 	if (tmp1)
537 		free(tmp1);
538 	if (tmp2)
539 		free(tmp2);
540 }
541 
542