]> mj.ucw.cz Git - libucw.git/blob - charset/charconv.c
Added functions for reading/writing UTF-8 characters on fastbuf streams.
[libucw.git] / charset / charconv.c
1 /*
2  *      Character Set Conversion Library 1.1
3  *
4  *      (c) 1998--2001 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU 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 static int
30 conv_from_utf8(struct conv_context *c)
31 {
32   unsigned short *x_to_out = c->x_to_out;
33   const unsigned char *s = c->source;
34   const unsigned char *se = c->source_end;
35   unsigned char *d = c->dest;
36   unsigned char *de = c->dest_end;
37   unsigned char *strings = string_table - 0x100;
38   unsigned int counter, code, cc;
39
40   if (c->state)
41     goto go_slow;
42
43   while (s < se)                        /* Optimized for speed, beware of spaghetti code */
44     {
45       cc = *s++;
46       if (cc < 0x80)
47         code = cc;
48       else if (cc >= 0xc0)
49         {
50           if (s + 6 > se)
51             goto go_slow_1;
52           if (cc < 0xe0)
53             {
54               if ((s[0] & 0xc0) != 0x80)
55                 goto nocode;
56               code = cc & 0x1f;
57               code = (code << 6) | (*s++ & 0x3f);
58             }
59           else if (cc < 0xf0)
60             {
61               if ((s[0] & 0xc0) != 0x80 || (s[1] & 0xc0) != 0x80)
62                 goto nocode;
63               code = cc & 0x0f;
64               code = (code << 6) | (*s++ & 0x3f);
65               code = (code << 6) | (*s++ & 0x3f);
66             }
67           else if (cc < 0xfc)
68             {
69               while (cc & 0x80)
70                 {
71                   if ((*s++ & 0xc0) != 0x80)
72                     break;
73                   cc <<= 1;
74                 }
75               goto nocode;
76             }
77           else
78             goto nocode;
79         }
80       else
81         {
82         nocode:
83           code = 0xfffd;
84         }
85     uni_again:
86       code = x_to_out[uni_to_x[code >> 8U][code & 0xff]];
87     code_again:
88       if (code < 0x100)
89         {
90           if (d >= de)
91             goto dend;
92           *d++ = code;
93         }
94       else
95         {
96           unsigned char *k = strings + code;
97           unsigned int len = *k++;
98
99           if (d + len > de)
100             goto dend;
101           while (len--)
102             *d++ = *k++;
103         }
104     }
105   c->state = 0;
106 send_noreset:
107   c->source = s;
108   c->dest = d;
109   return CONV_SOURCE_END;
110
111 dend:
112   c->state = ~0;
113   c->value = code;
114   c->source = s;
115   c->dest = d;
116   return CONV_DEST_END;
117
118 go_slow:
119   code = c->value;
120   counter = c->state;
121   if (counter == ~0U)
122     goto code_again;
123   goto go_slow_2;
124
125 go_slow_1:
126   if (cc < 0xe0) { code = cc & 0x1f; counter = 1; }
127   else if (cc < 0xf0) { code = cc & 0x0f; counter = 2; }
128   else
129     {
130       code = ~0;
131       if (cc < 0xf8) counter = 3;
132       else if (cc < 0xfc) counter = 4;
133       else if (cc < 0xfe) counter = 5;
134       else goto nocode;
135     }
136 go_slow_2:
137   while (counter)
138     {
139       if (s >= se)
140         {
141           c->state = counter;
142           c->value = code;
143           goto send_noreset;
144         }
145       if ((*s & 0xc0) != 0x80)
146         goto nocode;
147       code = (code << 6) | (*s++ & 0x3f);
148       counter--;
149     }
150   if (code >= 0x10000)
151     goto nocode;
152   goto uni_again;
153 }
154
155 static int
156 conv_to_utf8(struct conv_context *c)
157 {
158   unsigned short *in_to_x = c->in_to_x;
159   const unsigned char *s = c->source;
160   const unsigned char *se = c->source_end;
161   unsigned char *d = c->dest;
162   unsigned char *de = c->dest_end;
163
164   while (s < se)
165     {
166       unsigned int code = x_to_uni[in_to_x[*s]];
167       if (code < 0x80)
168         {
169           if (d >= de)
170             goto dend;
171           *d++ = code;
172         }
173       else if (code < 0x800)
174         {
175           if (d + 2 > de)
176             goto dend;
177           *d++ = 0xc0 | (code >> 6);
178           *d++ = 0x80 | (code & 0x3f);
179         }
180       else
181         {
182           if (d + 3 > de)
183             goto dend;
184           *d++ = 0xc0 | (code >> 12);
185           *d++ = 0x80 | ((code >> 6) & 0x3f);
186           *d++ = 0x80 | (code & 0x3f);
187         }
188       s++;
189     }
190   c->source = s;
191   c->dest = d;
192   return CONV_SOURCE_END;
193
194 dend:
195   c->source = s;
196   c->dest = d;
197   return CONV_DEST_END;
198 }
199
200 static int
201 conv_standard(struct conv_context *c)
202 {
203   unsigned short *in_to_x = c->in_to_x;
204   unsigned short *x_to_out = c->x_to_out;
205   const unsigned char *s = c->source;
206   const unsigned char *se = c->source_end;
207   unsigned char *d = c->dest;
208   unsigned char *de = c->dest_end;
209   unsigned char *strings = string_table - 0x100;
210
211   while (s < se)
212     {
213       unsigned int code = x_to_out[in_to_x[*s]];
214       if (code < 0x100)
215         {
216           if (d >= de)
217             goto dend;
218           *d++ = code;
219         }
220       else
221         {
222           unsigned char *k = strings + code;
223           unsigned int len = *k++;
224
225           if (d + len > de)
226             goto dend;
227           while (len--)
228             *d++ = *k++;
229         }
230       s++;
231     }
232   c->source = s;
233   c->dest = d;
234   return CONV_SOURCE_END;
235
236 dend:
237   c->source = s;
238   c->dest = d;
239   return CONV_DEST_END;
240 }
241
242 void
243 conv_set_charset(struct conv_context *c, int src, int dest)
244 {
245   if (src == dest)
246     c->convert = conv_none;
247   else
248     {
249       c->convert = conv_standard;
250       if (src == CONV_CHARSET_UTF8)
251         c->convert = conv_from_utf8;
252       else
253         c->in_to_x = input_to_x[src];
254       if (dest == CONV_CHARSET_UTF8)
255         c->convert = conv_to_utf8;
256       else
257         c->x_to_out = x_to_output[dest];
258     }
259   c->state = 0;
260 }
261
262 unsigned int
263 conv_x_to_ucs(unsigned int x)
264 {
265   return x_to_uni[x];
266 }
267
268 unsigned int
269 conv_ucs_to_x(unsigned int ucs)
270 {
271   return uni_to_x[ucs >> 8U][ucs & 0xff];
272 }
273
274 unsigned int
275 conv_x_count(void)
276 {
277   return sizeof(x_to_uni) / sizeof(x_to_uni[0]);
278 }