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