]> mj.ucw.cz Git - libucw.git/blob - charset/charconv.c
tested MJ's patches on charconv. they successfully converted everything
[libucw.git] / charset / charconv.c
1 /*
2  *      Character Set Conversion Library 1.2
3  *
4  *      (c) 1998--2004 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include "lib/lib.h"
11 #include "charset/charconv.h"
12 #include "charset/chartable.h"
13
14 void
15 conv_init(struct conv_context *c)
16 {
17   c->source = c->source_end = NULL;
18   c->dest = c->dest_start = c->dest_end = NULL;
19 }
20
21 static int
22 conv_none(struct conv_context *c)
23 {
24   c->dest_start = (char *) c->source;
25   c->dest = (char *) c->source_end;
26   return CONV_SOURCE_END | CONV_DEST_END | CONV_SKIP;
27 }
28
29 enum state {
30   CLEAN,
31   SINGLE_WRITE,
32   SEQ_WRITE,
33   UTF8_READ,
34   UTF8_WRITE_START,
35   UTF8_WRITE_CONT
36 };
37
38 static int
39 conv_slow(struct conv_context *c)
40 {
41   const unsigned char *s = c->source;
42   const unsigned char *se = c->source_end;
43   unsigned char *d = c->dest;
44   unsigned char *de = c->dest_end;
45
46   switch (c->state)
47     {
48     case SINGLE_WRITE:
49       if (d >= de)
50         goto cde;
51       *d++ = c->code;
52       break;
53     case SEQ_WRITE:
54       while (c->remains)
55         {
56           if (d >= de)
57             goto cde;
58           *d++ = *c->string_at++;
59           c->remains--;
60         }
61       break;
62     case UTF8_READ:
63       while (c->remains)
64         {
65           if (s >= se)
66             goto cse;
67           if ((*s & 0xc0) != 0x80)
68             {
69               c->code = 0xfffd;
70               break;
71             }
72           c->code = (c->code << 6) | (*s++ & 0x3f);
73           c->remains--;
74         }
75       if (c->code >= 0x10000)
76         c->code = 0xfffd;
77       c->source = s;
78       c->state = 0;
79       return -1;
80     case UTF8_WRITE_START:
81       if (d >= de)
82         goto cde;
83       if (c->code < 0x80)
84         {
85           *d++ = c->code;
86           break;
87         }
88       else if (c->code < 0x800)
89         {
90           *d++ = 0xc0 | (c->code >> 6);
91           c->code <<= 10;
92           c->remains = 1;
93         }
94       else
95         {
96           *d++ = 0xe0 | (c->code >> 12);
97           c->code <<= 4;
98           c->remains = 2;
99         }
100       c->state = UTF8_WRITE_CONT;
101       /* fall-thru */
102     case UTF8_WRITE_CONT:
103       while (c->remains)
104         {
105           if (d >= de)
106             goto cde;
107           *d++ = 0x80 | (c->code >> 10);
108           c->code <<= 6;
109           c->remains--;
110         }
111       break;
112     default:
113       ASSERT(0);
114     }
115   c->source = s;
116   c->dest = d;
117   c->state = 0;
118   return 0;
119
120  cse:
121   c->source = s;
122   return CONV_SOURCE_END;
123
124  cde:
125   c->dest = d;
126   return CONV_DEST_END;
127 }
128
129 static int
130 conv_from_utf8(struct conv_context *c)
131 {
132   unsigned short *x_to_out = c->x_to_out;
133   const unsigned char *s, *se;
134   unsigned char *d, *de, *k;
135   unsigned int code, cc, len;
136   int e;
137
138   if (unlikely(c->state))
139     goto slow;
140
141 main:
142   s = c->source;
143   se = c->source_end;
144   d = c->dest;
145   de = c->dest_end;
146   while (s < se)                        /* Optimized for speed, beware of spaghetti code */
147     {
148       cc = *s++;
149       if (cc < 0x80)
150         code = cc;
151       else if (cc >= 0xc0)
152         {
153           if (s + 6 > se)
154             goto send_utf;
155           if (cc < 0xe0)
156             {
157               if ((s[0] & 0xc0) != 0x80)
158                 goto nocode;
159               code = cc & 0x1f;
160               code = (code << 6) | (*s++ & 0x3f);
161             }
162           else if (cc < 0xf0)
163             {
164               if ((s[0] & 0xc0) != 0x80 || (s[1] & 0xc0) != 0x80)
165                 goto nocode;
166               code = cc & 0x0f;
167               code = (code << 6) | (*s++ & 0x3f);
168               code = (code << 6) | (*s++ & 0x3f);
169             }
170           else if (cc < 0xfc)
171             {
172               while (cc & 0x80)
173                 {
174                   if ((*s++ & 0xc0) != 0x80)
175                     break;
176                   cc <<= 1;
177                 }
178               goto nocode;
179             }
180           else
181             goto nocode;
182         }
183       else
184         {
185         nocode:
186           code = 0xfffd;
187         }
188     got_code:
189       code = x_to_out[uni_to_x[code >> 8U][code & 0xff]];
190       if (code < 0x100)
191         {
192           if (d >= de)
193             goto dend_char;
194           *d++ = code;
195         }
196       else
197         {
198           k = string_table + code - 0x100;
199           len = *k++;
200           if (d + len > de)
201             goto dend_str;
202           while (len--)
203             *d++ = *k++;
204         }
205     }
206   c->source = s;
207   c->dest = d;
208   return CONV_SOURCE_END;
209
210 send_utf:
211   c->state = UTF8_WRITE_START;
212   if (cc < 0xe0)                { c->code = cc & 0x1f; c->remains = 1; }
213   else if (cc < 0xf0)           { c->code = cc & 0x0f; c->remains = 2; }
214   else
215     {
216       c->code = ~0U;
217       if (cc < 0xf8)            c->remains = 3;
218       else if (cc < 0xfc)       c->remains = 4;
219       else if (cc < 0xfe)       c->remains = 5;
220       else goto nocode;
221     }
222   goto go_slow;
223
224 dend_str:
225   c->state = SEQ_WRITE;
226   c->string_at = k;
227   c->remains = len;
228
229 dend_char:
230   c->state = SINGLE_WRITE;
231   c->code = code;
232   goto go_slow;
233 go_slow:
234   c->source = s;
235   c->dest = d;
236 slow:
237   e = conv_slow(c);
238   if (e < 0)
239     {
240       code = c->code;
241       s = c->source;
242       se = c->source_end;
243       d = c->dest;
244       de = c->dest_end;
245       goto got_code;
246     }
247   if (e)
248     return e;
249   goto main;
250 }
251
252 static int
253 conv_to_utf8(struct conv_context *c)
254 {
255   unsigned short *in_to_x = c->in_to_x;
256   const unsigned char *s, *se;
257   unsigned char *d, *de;
258   unsigned int code;
259   int e;
260
261   if (unlikely(c->state))
262     goto slow;
263
264 main:
265   s = c->source;
266   se = c->source_end;
267   d = c->dest;
268   de = c->dest_end;
269   while (s < se)
270     {
271       code = x_to_uni[in_to_x[*s]];
272       if (code < 0x80)
273         {
274           if (d >= de)
275             goto dend;
276           *d++ = code;
277         }
278       else if (code < 0x800)
279         {
280           if (d + 2 > de)
281             goto dend_utf;
282           *d++ = 0xc0 | (code >> 6);
283           *d++ = 0x80 | (code & 0x3f);
284         }
285       else
286         {
287           if (d + 3 > de)
288             goto dend_utf;
289           *d++ = 0xe0 | (code >> 12);
290           *d++ = 0x80 | ((code >> 6) & 0x3f);
291           *d++ = 0x80 | (code & 0x3f);
292         }
293       s++;
294     }
295   c->source = s;
296   c->dest = d;
297   return CONV_SOURCE_END;
298
299 dend:
300   c->source = s;
301   c->dest = d;
302   return CONV_DEST_END;
303
304 dend_utf:
305   c->source = s;
306   c->dest = d;
307   c->state = UTF8_WRITE_START;
308   c->code = code;
309 slow:
310   e = conv_slow(c);
311   if (e)
312     return e;
313   goto main;
314 }
315
316 static int
317 conv_standard(struct conv_context *c)
318 {
319   unsigned short *in_to_x = c->in_to_x;
320   unsigned short *x_to_out = c->x_to_out;
321   const unsigned char *s, *se;
322   unsigned char *d, *de, *k;
323   unsigned int len, e;
324
325   if (unlikely(c->state))
326     goto slow;
327
328 main:
329   s = c->source;
330   se = c->source_end;
331   d = c->dest;
332   de = c->dest_end;
333   while (s < se)
334     {
335       unsigned int code = x_to_out[in_to_x[*s]];
336       if (code < 0x100)
337         {
338           if (unlikely(d >= de))
339             goto dend;
340           *d++ = code;
341         }
342       else
343         {
344           k = string_table + code - 0x100;
345           len = *k++;
346           if (unlikely(d + len > de))
347             goto dend_str;
348           while (len--)
349             *d++ = *k++;
350         }
351       s++;
352     }
353   c->source = s;
354   c->dest = d;
355   return CONV_SOURCE_END;
356
357 dend:
358   c->source = s;
359   c->dest = d;
360   return CONV_DEST_END;
361
362 dend_str:
363   c->source = s;
364   c->dest = d;
365   c->state = SEQ_WRITE;
366   c->string_at = k;
367   c->remains = len;
368 slow:
369   e = conv_slow(c);
370   if (e)
371     return e;
372   goto main;
373 }
374
375 void
376 conv_set_charset(struct conv_context *c, int src, int dest)
377 {
378   c->source_charset = src;
379   c->dest_charset = dest;
380   if (src == dest)
381     c->convert = conv_none;
382   else
383     {
384       c->convert = conv_standard;
385       if (src == CONV_CHARSET_UTF8)
386         c->convert = conv_from_utf8;
387       else
388         c->in_to_x = input_to_x[src];
389       if (dest == CONV_CHARSET_UTF8)
390         c->convert = conv_to_utf8;
391       else
392         c->x_to_out = x_to_output[dest];
393     }
394   c->state = 0;
395 }
396
397 unsigned int
398 conv_x_to_ucs(unsigned int x)
399 {
400   return x_to_uni[x];
401 }
402
403 unsigned int
404 conv_ucs_to_x(unsigned int ucs)
405 {
406   return uni_to_x[ucs >> 8U][ucs & 0xff];
407 }
408
409 unsigned int
410 conv_x_count(void)
411 {
412   return sizeof(x_to_uni) / sizeof(x_to_uni[0]);
413 }